Parameters not being set in native query - java

I am working on a SpringBoot Application and I am trying to do a simple delete using: #Query(value="", nativeQuery = true)
My query has dynamic parameters but the parameters are not getting substituted and I am also not getting any error on console.
I tried the NativeQuery with hardcoded parameters and it worked perfectly fine.
Below is the code:
#Repository
public interface TypGbPccRepo extends JpaRepository<TypGbPcc, String>{
#Transactional
#Modifying(ClearAutomatically = true)
#Query(value = "delete from T_ST_KFR__TYP_GB_PCC typ where Trunc(typ.GUELTIG_AB) IN (?1) and typ.GB_PCC IN (?2)" , nativeQuery=true)
void deleteRecords(String datumStr , String gbPccStr);
}
I even tried the below query:
#Query(value = "delete from T_ST_KFR__TYP_GB_PCC typ where Trunc(typ.GUELTIG_AB) IN (:datumStr ) and typ.GB_PCC IN (:gbPccStr)" , nativeQuery=true)
void deleteRecords(#Param("datumStr ") String datumStr , #Param("gbPccStr") String gbPccStr);
Parameters I am trying to substitute:
datumStr - TO_DATE('12-Sep-2012', 'dd-MM-yy'), TO_DATE('14-Sep-2012', 'dd-MM-yy')
gbPccStr - 'P0','P1'
I am implementing the above code to delete records based on a Composite Primary key which is a combination of GUELTIG_AB & GB_PCC. If possible suggest me how to achieve this with a query QueryMethod?
I have been able to delete records based on Primary which is not composite by using QueryMethod but I am unable to do this for a composite Primary key. I have to delete Multiple records in one go.
Below is my POJO
#Entity
#IdClass(TypGbPccId.class)
#Table(name="T_ST_KFR_TYP_GB_PCC")
public class TypGbPcc implements Serializable{
private static final long serialVersionUID = .....
#Id
#Column(name="GB_PCC")
private String gbPcc = null ;
#Column(name="GB_PCC_desc")
priavte String gbPccDesc = null ;
#Column(name="GB_PCC_SQL")
priavte String gbPccSql = null ;
#Id
#Column(name="GUELTIG_AB")
#JsonFormat(pattern = "dd-MM-yyyy")
private String gueltigAb = null ;
}
setter & getters here

These are bind parameters, they get passed to the database separately from the SQL statement. They are not used to create a new SQL statement with the parameters concatenated into it.
So what your Trunc(typ.GUELTIG_AB) IN (?1) with the given parameter really boils down to is:
Throw away the time part from GUELTIG_AB, convert it to a String using the systems default format, whatever that happens to be and check if the resulting string is contained in the list consisting of a single string: "TO_DATE('12-Sep-2012' , 'dd-MM-yy'),TO_DATE('14-Sep-2012' , 'dd-MM-yy')"
What you probably want is closer to:
Is GUELTIG_AB element in the list of dates with the two elements 12th of September 2014 and 14th of September 2014
Therefore your method should look like:
#Modifying(ClearAutomatically = true)
#Query(value = "delete from T_ST_KFR__TYP_GB_PCC typ " +
"where Trunc(typ.GUELTIG_AB) IN (?1) " +
"and typ.GB_PCC IN (?2)"
, nativeQuery=true)
void deleteRecords(List<java.sql.Date> dates , List<String> gbPccStr);
Note: you don't need the #Transactional annotation on the method, nor the #Repository annotation on the interface.
I am implementing the above code to delete records based on a Composite Primary key which is a combination of GUELTIG_AB & GB_PCC. If possible suggest me how to achieve this with a query QueryMethod?
That is not at all what the current query is doing.
If you have rows with the following keys:
(d1,p1)
(d1,p2)
(d2,p1)
(d2,p2)
You can't delete for example (d1,p1) and (d2,p2) with this approach, because it will also delete the other two elements.
In order to fix that declare an #IdClass on your entity. Specify that in the type parameters for your Repository and use a normal query method.

First of all there is a space after datumStr in #Param("datumStr "). Next, change its type to List of String and pass the date strings in your default DB date format.
(You should change the type of gbPccStr to List of Sting as well)

You can do something like this.
#Query(value = "delete from TypGbPcc where func('TRUNC',gueltigAb) IN func('TO_DATE',:dateOne,'dd/mm/yyyy') and
func('TRUNC',gueltigAb) IN func('TO_DATE',:dateTwo,'dd/mm/yyyy') and gbPcc IN (:gbPccStr)" , nativeQuery=true)
void deleteRecords(#Param("dateOne") String dateOne ,#Param("dateTwo") String dateTwo, #Param("gbPccStr") String gbPccStr);
in dateOne give first date in dateTwo give second date

Related

Is there a way to return collection instead of entity in #Repository's #Query

I have a query:
#Query(
value = "select name, age, now() from received.scheme ;",
nativeQuery = true
)
public {???} selectData()
I cannot create or return an entity for such a scheme as there is no natural id in it, so is there a way to return something like List<Triple<String, Int, LocalDateTime>>?
you can create another class with the required properties which you want to retrieve from the database and then you can return that class as List<Class>.
In your code you get the data from: scheme
So the Entity SchemeEntity should contains those three fields:
name
age
now (creationDate for example it depend on your logic)
Then your method should be like this:
#Query(value = "select name, age, now() from received.scheme ;",
nativeQuery = true
)
public List<SchemeEntity> selectData();
You can provide a PROJECTION entity which will have 3 attributes and must provide a parametrized constructor with those 3 attributes you want to fetch ,or you can still get them as a List<Object[]> and then hydrate your entity.
I've tried those things but what worked for me is using ctid as an #Id. Since I only needed some sort of a mock id, it worked fine.

Select only specific columns from joined tables (Many-to-Many) in Spring Data JPA

The purpose is to select columns from joined tables (Many-to-Many).
The problem i have is to select two columns from a joined Many-to-Many table.
I'am using Springboot 2.3 and Spring data Jpa.
I have this data model, and what i want to fetch are the blue boxed fields
So the native query could look like this (if i am right ...)
SELECT bg.id, bg.name, p.name, c.name, c.short_desc FROM boardgame as bg
JOIN boardgame_category bgc on bg.id = bgc.fk_game
JOIN publisher p on bg.fk_publisher = p.id
JOIN category c on bgc.fk_category = c.id
WHERE bg.id = :id
I first tried to work with dto in JPQL statment
public class BoardgameDto {
private long id;
private String name;
private String publisherName;
private Set<CatregoryDto> categoryDto;
// setter, getter etc...
}
public class CategoryDto {
private String name;
private String shortDesc;
// setter, getter etc...
}
The JQPL query could look like this , but it doesn't work (IDE shows errors on CategoryDto)
/* THIS DOESN'T WORK */
SELECT new org.moto.tryingstuff.dto.BoardgameDto(bg.id, bg.name, p.name,
new org.moto.tryingstuff.dto.CategoryDto(c.name, c.short_desc)) FROM Boardgame as bg, Publisher as p, Category as c
Well, I think the problem I have with this way of doing is that the dto's contructor can't receive a collection as written here, and i think neither another contructor in parameter.
Then i started looking at Criteria Queries, especialy multiselect, Tuple, Dto, but it look like i had same kind of problems so i didn't dive deeper into it.
Finally i used a JpaRepository and it's findById() method like this
public interface BoardgameRepository extends JpaRepository<Boardgame, Long> {
}
// In a test or service method
Boardgame game = repository.findById(long id);
Then i filter the fields i need to keep through mappings in Service or Controller layer. So the front only received need datas.
But it feel a bit overkill,
Am I missing something, any part of the framework that would allow me to select only specific columns?
As you wrote, you can't use a collection as the parameter of a constructor expression. That's because the expression gets applied to each record in the result set. These records are a flat data structure. They don't contain any collections. Your database returns a new record for each element in that collection instead.
But your constructor expression fails for a different reason. You're trying to combine 2 constructor expressions, and that's not supported. You need to remove the 2nd expression and perform that operation within the constructor of your DTO.
So, your query should look like this:
SELECT new org.moto.tryingstuff.dto.BoardgameDto(bg.id, bg.name, p.name, c.name, c.short_desc) FROM Boardgame as bg <Your JOIN CLAUSES HERE>
And the constructor of your BoardgameDto like this:
public class BoardgameDto {
public BoardgameDto(Long id, String gameName, String publisherName, String categoryName, String description) {
this.id = id;
this.name = gameName;
this.publisherName = publisherName;
this.category = new Category(categoryName, description);
}
...
}

ClassCastException caused by wrapping data type in Optional

Problem: I get the following error log -> java.lang.ClassCastException: java.sql.Timestamp cannot be cast to java.time.LocalDateTime
Problem possible caused by : The return type is Optional which means jpa having difficulties mapping it since it recognizes optional and not the actual LocalDateTime data type.
Did anyone have any thoughts on how to fix this? i mean i could return an object and its working just fine but i simply need a single value to be returned
works:
#Query(value = "SELECT * from audit where type='REQUEST_DOC_ONLINE' and user_id=:userId order by timestamp desc limit 1", nativeQuery = true)
Optional<AuditEntity> getLatestRetrievalDocumentDateAndTime(#Param("userId") Long userId);
does not work:
#Query(value = "SELECT * from audit where type='REQUEST_DOC_ONLINE' and user_id=:userId order by timestamp desc limit 1", nativeQuery = true)
Optional<LocalDateTime> getLatestRetrievalDocumentDateAndTime(#Param("userId") Long userId);
You are fetching all columns and trying to cast it to LocalDateTime. I would rather suggest you select only the columns which have LocalDateTime as a datatype.
I also think you are probably using Timestamp or Instant as datatype in the AuditEntity class. If so, try using the same class while fetching.

Problems mapping Hibernate entities - native query containing left join with condition

This should be straight-forward though can't get my Hibernate entities to play nice for the following scenario with a simple two table structure:
I'm attempting to get all config names and matching config values for a given currency code (and null's where not matching).. so have written a native query to retrieve the following like so:
SELECT * FROM CONFIG_NAME LEFT JOIN CONFIG_VALUE ON CONFIG_NAME.ID =
CONFIG_VALUE.CONFIG_ID AND CONFIG_VALUE.CURRENCY_CODE = '<CURRENCY_CODE>'
ORDER BY CONFIG_NAME.ID
This query doesn't seem to play nice with my Hibernate mapping as it appears to be essentially ignoring the CURRENCY_CODE clause in the join.
Essentially, for the following subset of data:
CONFIG_NAME:
CONFIG_VALUE:
There is no value defined for 'FREE_SHIPPING_ENABLED' for 'USD' so running the query above for both currency code returns as expected:
QUERY RESULTS FOR 'CAD':
QUERY RESULTS FOR 'USD':
I'm running the above query as a native query in a JpaRepository for the ConfigName entity. But what I appear to be getting is that it seems to ignore the currency_code clause in the JOIN condition. As the list of config values defined has both values for USD and CAD where they're populated. Is there an Hibernate annotation to factor this in that I'm unaware of?
It's worth bearing in mind there will only ever be ONE value defined for each config for a given currency - there's a unique constraint across CONFIG_VALUE.CONFIG_ID/CONFIG_VALUE.CURRENCY_CODE so potentially ConfigValue on the ConfigName entity would not need to be a map.
Mappings as are follows:
ConfigName - Entity
#OneToMany(mappedBy = "config")
private Set<ConfigValue> configValue;
ConfigValue - Entity
#ManyToOne(optional = false)
#JoinColumn(name="CONFIG_ID")
#Property(policy=PojomaticPolicy.NONE)
private ConfigName config;
Doesn't need to be strictly unidirectional either.. as I'm only concerned with the values from the ConfigName entity either being populated or null.
Think I'm missing something simple, so hope someone can help.
EDIT: Am querying using JpaRepository:
Am using JpaRepository to query:
#Repository
public interface ConfigNameRepository extends JpaRepository<ConfigName, Long>
{
static final String SQL_QUERY = "SELECT * FROM CONFIG_NAME "
+ "LEFT JOIN CONFIG_VALUE ON CONFIG_NAME.ID = CONFIG_VALUE.CONFIG_ID "
+ "AND CONFIG_VALUE.CURRENCY_CODE = ?1 ORDER BY CONFIG_NAME.ID";
#Query(value = SQL_QUERY, nativeQuery = true)
List<ConfigName> findConfigValuesByCurrencyCode(final String currencyCode);
}
As mentioned by #Ouney, your JPA relations are not taken in account if you use a native query.
You declared a SELECT * and List<ConfigName> (the real sql result contains ConfigName+ConfigValue). So with this query, Hibernate fetchs all the ConfigName. Then, when you try to access to the set of configValue, it fetchs all the related ConfigValue.
I think this should be better/easier to use a JPQL query instead (but you need Hibernate 5.1+) :
SELECT n, v
FROM ConfigName n
LEFT JOIN ConfigValue v
ON v.config = n AND v.currencyCode = :currencyCode
ORDER BY n.id
With this method signature :
List<Object[]> findConfigValuesByCurrencyCode(#Param("currencyCode") String currencyCode);
Where the result will be :
o[0] // ConfigName
o[1] // ConfigValue (nullable)
You may want to do this prettier with a wrapper :
SELECT new my.package.MyWrapper(n, v)
...
MyWrapper constructor :
public MyWrapper(ConfigName configName, ConfigValue configValue) {
...
}
Method signature with the wrapper :
List<MyWrapper> findConfigValuesByCurrencyCode(#Param("currencyCode") String currencyCode);
(update)
I think in this case, your query can be :
SELECT n, v // or new my.package.MyWrapper(n, v)
FROM ConfigName n
LEFT JOIN n.configValue v
WITH v.currencyCode = :currencyCode
ORDER BY n.id

Hibernate criteria, distinct association property

Say I have at least these two entities:
class Person {
String firstname, lastname;
Address address;
ManyOtherPropertiesAndEntities ...;
}
class Address {
String street;
Country country;
}
Now, I would like to query the Person table and ONLY Persons that live on different streets.
That is, ignore all Persons that live on same street, and return only one of these Person, any one.
How can I perform such a query?
Is that possibly using Criteria?
Criteria criteria = session.createCriteria(Person.class, "person");
Criteria addressCriteria = criteria.createCriteria("address")
criteria.setProjection(
Projections.distinct(
Projections.projectionList().add(Projections.property("address.street"))
)
);
This doesnt really work.
I've also tried to do:
projectionList.add( Projections.sqlProjection("DISTINCT ON ( address.street ), ... ", columns.toArray(new String[columns.size()]), types.toArray(new Type[types.size()])));
But also fruitless.
>>>>>>>>>>>>>>>>>>>>>>>EDIT<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
I was able to get this to run, and generate an SQL query that actually returns results in a pure sql mode, but seems to return zero in hibernate:
List<String> columns = Lists.lst();
List<Type> types = Lists.lst();
bondCriteria.setProjection(
Projections.sqlProjection ("DISTINCT ON ( tvmux2_.polarization ) * "
, columns.toArray (new String[columns.size()])
, types.toArray (new Type[types.size()])
)
// Projections.projectionList().add(Projections.distinct(Projections.property("polarization")))
);
ResultTransformer resultTransformer = new ResultTransformer() {
#Override
public List transformList( List collection ) {
return null;
}
#Override
public Object transformTuple( Object[] tuple, String[] aliases ) {
return null;
}
};
bondCriteria.setResultTransformer(resultTransformer);
* ITS 2017 and SO still hasn't included a proper editor to easily be able to format code so that indentation and copy and paste is not a complete hell. Feel free to scroll horizontally. *
THis generates teh following query basically
select DISTINCT ON ( tvmux2_.polarization ) * from TvChannelBond this_ inner join TvChannel tvchannel1_ on this_.channel=tvchannel1_.id inner join TvMux tvmux2_ on this_.mux=tvmux2_.id where this_.enabled=true order by tvmux2_.polarization asc limit 100
which does return results in a non hibernate mode.
However, since sqlProjection method requires the supplementation of 3 params, I am not sure what to add to the second and third params. Types can not be other than predefined Hibernate types just as DOUBLE, STRING and so on.
When debugging into the resultTransformer, it gets into transformTuple with zero length tuple[] and aliases[].
Might have to do with the sqlProjection zero length types and columns lists.
In SQL, you could do it like this:
SELECT p.*
FROM Address a
INNER JOIN Person p ON ...
GROUP BY a.Street
HAVING p.id = MIN(p.id)
This statement selects for every distinct Street from Address the Person with the minimum id value. Instead of MIN(p.id) you can of course use any other field and aggregate function which will match exactly one person per street; MAX(p.id) will work, MIN(p.lastname) won't if there can be more than one "Smith" in a street.
Can you transform the above SQL to your Criteria query?

Categories

Resources