Spring Data and native query with like statement - java

I have the following repository definition:
public interface InstructionRepository extends JpaRepositoryWithSpecification<Instruction> {
String FIND_INSTRUCTIONS_BY_TRACKING_ID_QUERY =
"SELECT * FROM instruction i WHERE i.invoice_documents_trackings like '%:trackingId%'";
#Query(value = FIND_INSTRUCTIONS_BY_TRACKING_ID_QUERY, nativeQuery = true)
List<Instruction> findByFileTrackingsContaining(#Param("trackingId") String trackingId);
}
The reason why I use native query here is because invoice_documents_trackings column represents a map serialized to json string. So basically I want to find all instructions that have particular trackingId stored in the invoice_documents_trackings map.
When I execute the method I always get 0 results despite the fact that If I execute the same query manually I get expected results.
I also tried to change the query so that it looks like:
String FIND_INSTRUCTIONS_BY_TRACKING_ID_QUERY =
"SELECT * FROM instruction i WHERE i.invoice_documents_trackings like %:trackingId%"
And this does not work either.
Would really appreciate any help, than

I think the issue is the way you're using %
String FIND_INSTRUCTIONS_BY_TRACKING_ID_QUERY =
"SELECT * FROM instruction i WHERE i.invoice_documents_trackings like CONCAT('%', :trackingId, '%')"

Related

UPDATE AND REPLACE part or a string/record with parameter

ALL>
I have here a named query to update and replace records.
#NamedQuery(name = POST.UPDATE_POST_MESSAGE, query = "UPDATE Post p SET p.message = REPLACE(p.message, :value, 'ANONYMOUS')"
I wanted the "old string" to be parameterized but it shows an error of
Caused by: java.lang.IllegalArgumentException: You have attempted to set a parameter value using a name of value that does not exist in the query string UPDATE Post p SET p.message = REPLACE(p.message, :value, 'ANONYMOUS').
here's the code in my dao layer:
private static final String VALUE = "value";
public void updateMessage(String value) {
EntityManager entityManager = createEntityManager();
entityManager.createNamedQuery(POST.UPDATE_POST_MESSAGE)
.setParameter(VALUE, value)
.executeUpdate();
}
I am not sure if we can use a parameter inside the replace function, been searching everywhere and i cant find an answer.
If not possible, can someone help/recommend a way to replace records using a parameter.
Here you assigned parameter value to VALUE variable but using value((lower case ).

Parameters not being set in native query

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

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

HQL and joins - a faster way?

This part of my model is as follows:
IQCEntity has many Documents
DocumentCategory has many Documents
I am using Hibernate for my ORM.
Now, please consider the following method:
/**
* Get all documents in the supplied IQCEntity which are in the
* specified DocumentCategory.
* #param entity the {#link IQCEntity} which holds the Documents
* #param category the {#link DocumentCategory} which the Documents belong to
* #return Collection<{#link Document}>
*/
#SuppressWarnings("unchecked")
public Collection<Document> getDocuments(IQCEntity entity, DocumentCategory category) {
String q = "from Document d where d.documentCategory.documentCategoryId = :c and d.entity.entityId = :e";
Query query = session.createQuery(q);
query.setParameter("c", category.getDocumentCategoryId());
query.setParameter("e", entity.getEntityId());
List<Document> documents = (List<Document>)query.list();
Collections.sort(documents);
return documents;
}
This method works, and brings back the correct results, however it seems to be pretty slow.
If I look at the table structure in the database, the Document table has parent ids (of course it does - else how could it join!), documentCategory_documentCategoryId and entity_entityId.
We all know that in SQL the correct results can be achieved without any joins at all. How can the same be done in HQL?
I have tried this: (Note the _ instead of .)
String q = "from Document d where d.documentCategory_documentCategoryId = :c and d.entity_entityId = :e";
but the property is not found.
org.hibernate.QueryException: could not resolve property: documentCategory_documentCategoryId of: com.foo.bar.entities.Document
Is there some way to reference the join fields instead of object references?
To avoid the joins use the identifier property .id:
String q = "from Document d where d.documentCategory.id = :c and d.entity.id = :e";
But since you also have the referenced objects you can even write a shorter version, using entity and category as parameters:
String q = "from Document d where d.documentCategory = :c and d.entity = :e";
Query query = session.createQuery(q);
query.setParameter("c", category);
query.setParameter("e", entity);
In both versions Hibernate is able to figure out that it actually does not need to join.

Creating JOOQ query dynamically

I need to create a JOOQ SELECT query dynamically based on the set of parameters. I dont know how to append it dynamically.
Please help
Thanks in advance.
jOOQ has two types of APIs to construct queries.
The DSL API that allows for creating inline SQL statements in your Java code, e.g.
create.select(T.A, T.B).from(T).where(T.X.eq(3).and(T.Y.eq(5)));
The "model" API that allows for incremental SQL building. At any time, you can access the "model" API through the getQuery() method on a DSL query object
An example of what you want to do is given in the manual here:
https://www.jooq.org/doc/latest/manual/sql-building/sql-statements/dsl-and-non-dsl/
For instance, optionally adding a join:
DSLContext create = DSL.using(configuration);
SelectQuery query = create.selectQuery();
query.addFrom(AUTHOR);
// Join books only under certain circumstances
if (join)
query.addJoin(BOOK, BOOK.AUTHOR_ID.equal(AUTHOR.ID));
Result<?> result = query.fetch();
Or, optinally adding conditions / predicates:
query.addConditions(BOOK.TITLE.like("%Java%"));
query.addConditions(BOOK.LANGUAGE_CD.eq("en"));
UPDATE: Given your comments, that's what you're looking for:
// Retrieve search strings from your user input (just an example)
String titleSearchString = userInput.get("TITLE");
String languageSearchString = userInput.get("LANGUAGE");
boolean lookingForTitles = titleSearchString != null;
boolean lookingForLanguages = languageSearchString != null;
// Add only those conditions that the user actually provided:
if (lookingForTitles)
query.addConditions(BOOK.TITLE.like("%" + titleSearchString + "%"));
else if (lookingForLanguages)
query.addConditions(BOOK.LANGUAGE_CD.eq(languageSearchString));
Note, you can also use the Field.compare(Comparator, Object) methods:
// Initialise your dynamic arguments
Field<String> field = BOOK.TITLE;
Comparator comparator = Comparator.LIKE;
String value = "%" + titleSearchString + "%";
// Pass them to the field.compare() method
query.addConditions(field.compare(comparator, value));
For more info, consider the org.jooq.SelectQuery Javadoc

Categories

Resources