Composite DTO projections using a Constructor Expression and JPQL - mysql

I am trying to select specific columns from db into a composite class DTO by giving fully qualified names.
#Data
public class Temp {
String dName;
Temp2 value;
public Temp( String dName, Temp2 value) {
this.dName = dName;
this.value = value;
}
#Data
public static class Temp2 {
Integer day;
public Temp2(Integer day) {
this.day = day;
}
}
}
Query: SELECT new com.pojo.Temp ( t1.displayName, new
com.pojo.Temp.Temp2 (t3.day)) FROM table1 t1 JOIN table2 t2 ON t1.bId
= t2.id AND LEFT JOIN table3 t3 ON t1.g_id = t2.id
Error: `[2018-11-06 12:02:54] [main] ERROR o.h.hql.internal.ast.ErrorCounter.reportError - [ ] line 1:64: unexpected token: ,
[2018-11-06 12:02:54] [main] ERROR o.h.hql.internal.ast.ErrorCounter.reportError - [ ] line 1:64: unexpected token: ,
antlr.NoViableAltException: unexpected token: ,
at org.hibernate.hql.internal.antlr.HqlBaseParser.primaryExpression(HqlBaseParser.java:1009)
at org.hibernate.hql.internal.antlr.HqlBaseParser.atom(HqlBaseParser.java:3549)
at org.hibernate.hql.internal.antlr.HqlBaseParser.unaryExpression(HqlBaseParser.java:3401)
at org.hibernate.hql.internal.antlr.HqlBaseParser.multiplyExpression(HqlBaseParser.java:3273)
at org.hibernate.hql.internal.antlr.HqlBaseParser.additiveExpression(HqlBaseParser.java:2930)
at org.hibernate.hql.internal.antlr.HqlBaseParser.concatenation(HqlBaseParser.java:615)
at org.hibernate.hql.internal.antlr.HqlBaseParser.relationalExpression(HqlBaseParser.java:2697)
at org.hibernate.hql.internal.antlr.HqlBaseParser.equalityExpression(HqlBaseParser.java:2558)
at org.hibernate.hql.internal.antlr.HqlBaseParser.negatedExpression(HqlBaseParser.java:2522)
at org.hibernate.hql.internal.antlr.HqlBaseParser.logicalAndExpression(HqlBaseParser.java:2438)
at org.hibernate.hql.internal.antlr.HqlBaseParser.logicalOrExpression(HqlBaseParser.java:2403)
at org.hibernate.hql.internal.antlr.HqlBaseParser.expression(HqlBaseParser.java:2116)
at org.hibernate.hql.internal.antlr.HqlBaseParser.aliasedExpression(HqlBaseParser.java:2357)
at org.hibernate.hql.internal.antlr.HqlBaseParser.selectedPropertiesList(HqlBaseParser.java:1390)
at org.hibernate.hql.internal.antlr.HqlBaseParser.newExpression(HqlBaseParser.java:1434)
at org.hibernate.hql.internal.antlr.HqlBaseParser.selectClause(HqlBaseParser.java:1306)
at org.hibernate.hql.internal.antlr.HqlBaseParser.selectFrom(HqlBaseParser.java:1040)
at org.hibernate.hql.internal.antlr.HqlBaseParser.queryRule(HqlBaseParser.java:748)
at org.hibernate.hql.internal.antlr.HqlBaseParser.selectStatement(HqlBaseParser.java:319)
at org.hibernate.hql.internal.antlr.HqlBaseParser.statement(HqlBaseParser.java:198)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:284)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:186)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:141)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:115)
at org.hibernate.engine.query.spi.HQLQueryPlan.<init>(HQLQueryPlan.java:77)
at org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:153)
at org.hibernate.internal.AbstractSharedSessionContract.getQueryPlan(AbstractSharedSessionContract.java:553)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:662)
at org.hibernate.internal.AbstractSharedSessionContract.createQuery(AbstractSharedSessionContract.java:103)
`
I am not able to find any relevant answers for getting data into composite class dto using jpql expression. Since i am new i might be missing something.
Any kind help will be appreciated.

Nesting constructor expressions like this is not possible AFAIK. There's one way to do it that I know of, but it's a dirty and ugly work around. I put all the params in one constructor and then instantiated the other class objects inside the constructor.
Example:
public CommentDTO(Long id, String body, LocalDateTime datePosted,
LocalDateTime lastModifiedDate, Long userId,
String login, String avatarUrl, boolean hireable) {
this.id = id;
this.body = body;
this.datePosted = datePosted;
this.lastModifiedDate = lastModifiedDate;
this.author = new UserDTO(userId, login, avatarUrl, hireable);
So the JPQL query would have to have all of those params.
Again, wouldn't recommend doing it this way as it becomes 100% unmaintanable. But it's the only way I've found to do it with constructor expressions.
I just refactored to using interface based projections. If you're using Spring Data JPA, it's ridiculously easy to set it up for nested projections.

Related

Spring data reactive repository - r2dbc not working

The query is getting executed but not getting any result.
router :- api/v1/service/appt/usr/{usr_id}
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
public Mono<ServerResponse> retrieveProjectsByUsr(ServerRequest request) {
final String userIdStr = request.pathVariable(USER_ID_PARAM);
final Optional<String> stDateStr = request.queryParam("stDate");
final Optional<String> endDateStr = request.queryParam("endDate");
final LocalDateTime stDate = LocalDateTime.parse(stDateStr.get(), DATE_TIME_FORMATTER);
final LocalDateTime endDate = LocalDateTime.parse(endDateStr.get(), DATE_TIME_FORMATTER);
long userId = Long.parseLong(userIdStr);
return secContext.retrieveUser().flatMap(usr -> {
Flux<Appt> appts = projectRepository.findApptsBetween(stDate, endDate, userId, usr.getOrgId());
return ServerResponse.ok().contentType(APPLICATION_JSON).body(appts, Project.class);
});
}
Repository code,
#Repository
public interface ApptRepository extends ReactiveCrudRepository<Appt, Long> {
#Query("select * from appt where usr_id = :usrId and org_id = :orgId and start_time BETWEEN :stDate and :endDate")
Flux<Appt> findApptsBetween(LocalDateTime stDate, LocalDateTime endDate, long usrId, int orgId);
}
Query from the log,
Executing SQL statement [select * from appt where usr_id = :usrId and org_id = :orgId and start_time BETWEEN :stDate and :endDate]
Data in project table,
Postman request,
http://localhost:9090/api/v1/service/appt/usr/2?stDate=2021-01-24 03:20&endDate=2021-03-25 05:23
Not sure what is wrong with this. It doesn't return the record.
The problem here is that reactive code needs to be subscibed to, to start execution. The following statement only describes what should happen:
Flux<Appt> appts = projectRepository.findApptsBetween(stDate, endDate, userId, usr.getOrgId());
To initate execution one needs to add .subscribe() operator to the reactive call. But here you dont't want that because that will start execution in a different context/thread and you won't be able to return the value to the outer method. This is why one should write reactive code as chain of reactive calls.
(Note: controller methods and router functions have an implicit .subscribe() at the end of your code so you don't need to add it)
You could rewite this code to something like this:
return secContext.retrieveUser().flatMap(usr ->
projectRepository.findApptsBetween(stDate, endDate, userId, usr.getOrgId())
.collectList()
.map(appts -> ServerResponse.ok().contentType(APPLICATION_JSON).body(appts, Project.class));
The following code works. Answer was modified from the above posts.
return secContext.retrieveUser()
.flatMap(usr -> apptRepository.findApptsBetween(userId, usr.getOrgId(), stDate, endDate).collectList()
.flatMap(appts -> ServerResponse.ok().contentType(APPLICATION_JSON).bodyValue(appts)));

SQLGrammar error when querying MySql view

When a run a GET request i get an exception o.h.engine.jdbc.spi.SqlExceptionHelper : Unknown column 'disburseme0_.reason_type' in 'field list' in stack trace even though i have configured the field correctly in the entity class. I have a Spring Boot SOAP interface that is querying a MySql database view. I have assigned one of the unique keys from the parent tables as the view Id in JPA.
Part of my entity class has:
#Entity
#Table(name="disbursement_payload")
public class Disbursement {
#Id
#Column(name="ID")
private long disbursementId;
#Column(name="ReasonType")
private String reasonType;
public long getDisbursementId() {
return disbursementId;
}
public void setDisbursementId(long disbursementId) {
this.disbursementId = disbursementId;
public String getReasonType() {
return reasonType;
}
public void setReasonType(String reasonType) {
this.reasonType = reasonType;
}
I have the view as:
CREATE VIEW disbursement_payload AS (
SELECT
iso_number AS Currency,
trans_desc AS ReasonType,
account_number AS ReceiverParty,
amount AS Amount
FROM m_payment_detail, m_loan_transaction
WHERE m_payment_detail.`id`= m_loan_transaction.`payment_detail_id` AND
m_payment_detail.`payment_type_id`=2
);
Is there something im missing , in the entity or view definition? I have read one of the comments here could not extract ResultSet in hibernate that i might have to explicitly define the parent schemas. Any assistance, greatly appreciated.
do the mapping for db column and class var name based on camelCase conversion basded on underscore _ separated name
you could try using
CREATE VIEW disbursement_payload AS (
SELECT iso_number AS currency
, trans_desc AS reason_type
, account_number AS receiver_rarty
, amount AS amount
FROM m_payment_detail
INNER JOIN m_loan_transaction
ON m_payment_detail.`id`= m_loan_transaction.`payment_detail_id`
AND m_payment_detail.`payment_type_id`=2
);
the view code is SQL code and hibernate see a view as a table, so the conversion of column name is base on the same rules
and a suggestion you should not use (older) implicit join based on where condition you should use (more recent) explici join sintax ..

Using Cross join in Asp .net Web API getting error

Hi i am Using Cross join through in Asp .net Web API with a mysql database and getting the following error :
Error 1 Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Linq.IQueryable'. An explicit conversion exists (are you missing a cast?)
This is my controller code
private myappEntities db = new myappEntities();
public IQueryable<comment>GetPicturesandtheirCommnets()
{
var combo=from p in db.picturedetails
from c in db.comments
select new
{
p.iduser,p.idpictures,p.likes,p.nuditylevel,p.picTitle,p.pictime,p.fakeslevel,
c.comment1,c.ctime,c.idcomments,c.spamlevel,c.targetpictureid
};
return combo;
}
Why am i getting this error?? Any help?
Your query (combo) returns an anonymous type, and your method signature says you are returning an IQueryable<comment>. You can't return anonymous types from methods, so you have two options:
Option 1: Select just fields from the Comment table to return.
Option 2: Create a new class that includes details from Comments and PictureDetails, and modify your query to select new CommentAndPictureDetails (or whatever you name your class).
The modified query would look like this:
var combo=from p in db.picturedetails
from c in db.comments
select new CommentAndPictureDetails
{
IdUser = p.iduser,
IdPictures = p.idpictures,
Likes = p.likes,
NudityLevel = p.nuditylevel,
PicTitle = p.picTitle,
PicTime = p.pictime,
FakesLevel = p.fakeslevel,
Comment1 c.comment1,
CTime = c.ctime,
IdComments = c.idcomments,
SpamLevel = c.spamlevel,
TargetPictureId = c.targetpictureid
};
Your class declaration for CommentAndPictureDetails would be like so:
public class CommentAndPictureDetails
{
public string IdUser {get; set;}
// I don't know the data types, so you'll have to make sure
// the .NET type matches the DB type.
}

sqlexception index out of bounds with correct sql-statement

i've got an sql statement that works pretty well. but on implementing in my webapp working with play 2.1 i get this error:
javax.persistence.PersistenceException: Query threw SQLException:Column Index out of range, 0 < 1.
i found this question here: Error executing MySQL query via ebean using RawSql
but then i got other exceptions.
i'm trying to get tagged threads that contains a list of tags (same as stack overflow does).
here the sql statement
SELECT t.topic
FROM topic t
WHERE 3 = (SELECT COUNT( DISTINCT ta.id )
FROM topic_tag tt
INNER JOIN tag ta ON ta.id = tt.tag_id
WHERE ta.name IN ('children', 'spain','new')
AND tt.topic_id = t.id )
in play i do this:
RawSql rawSql = RawSqlBuilder.unparsed(sqlString).create();
result = find.setRawSql(rawSql).findList();
then, i got the out of bounds exception. after that i try to set column mappings:
RawSql rawSql = RawSqlBuilder.unparsed(sqlString)
.columnMapping("t.topic","topic")
.columnMapping("t.id","id")
.columnMapping("ta.name","tagList.name")
.columnMapping("ta.id","tagList.id")
.create();
now i get a null pointer exception. probably because ebean can't create a query from that.
here some code from my models:
#Entity
public class Topic extends Model{
#Id
public Long id;
#Required
public String topic;
#ManyToMany
public List<Tag> tagList;
}
#Entity
public class Tag extends Model {
#Id
public long id;
#Required
public String name;
}
after a lot of trying and frustrating i hope that somebody got a hint or a solution for this.
I just wasted few hours with similar problem, I actually managed to solve it by only mapping id field for certain kind of model and selecting lesser amount of fields, other values were automatically loaded after that - So basically, error occurred if I tried to select values like:
.. select e.id, e.name, e.description from exampleTable e .. and use mappings like:
RawSql rawSql = RawSqlBuilder.parse(sql)
// map the sql result columns to bean properties
.columnMapping("e.id", "exampleModel.id")
.columnMapping("e.name", "exampleModel.name")
.columnMapping("e.description", "exampleModel.description")
.create();
When I changed to select only e.id and map:
RawSql rawSql = RawSqlBuilder.parse(sql)
// map the sql result columns to bean properties
.columnMapping("e.id", "exampleModel.id")
.create();
It loaded also e.name and e.description to model values and errors disappeared.
(Of course my own query had several joins and were bit more complicated than this, but basics are the same.)
So to summarize: when this problem occurs, check that you are not loading anything twice (columnMapping), use System.out.println(""); or similar to check which values are already loaded for your model. Remember to also check annotations such as "#JoinColumn" which might load more data under same model - just based on given e.id value. If you dont select and set e.id as columnMapping value, then you might need to list all needed fields separately as .. e.name, e.description ..
Hopefully these findings helps someone out.

Select column from non-generic DbSet?

I want to implement a function that accepts a DbSet (non-generic), a string, and object, and returns DbSet. something like the following pseudu:
public static DbSet Any(DbSet set, string propertyName, objectParameter)
{
var tableName = set.TableName;
var columnName = set.GetColumnNameForProperty(propertyName);
var query = string.Format("SELECT TOP(1) {0} FROM {1} WHERE {0} = {2}",
columnName,
tableName,
objectParameter);
}
I think that SQL query is enough since I'll be able to execute it directly on the Database (context.Database.ExecuteSql).
What I want to do is get the table name from the given DbSet, then the column name in the database.
It is not possible from non generic DbSet but this problem can be easily solved by using:
public static IEnumerable<T> Any(DbSet<T> set, string property, objectParameter)
where T : class
{ ... }
Returning DbSet doesn't make sense because once you query data it is not DbSet anymore.
The bigger problem is getting table name from generic DbSet / ObjectSet because this information is not available from those classes. It is almost impossible to get it at all because it requires accessing non public members of items from MetadataWorkspace.