How can i convert the following native into Spring Data JPA interface method :
#Query(nativeQuery = true,
value = " select count(*) from TABLE "
+ "where ( ColumOne =:xyz or ColumnTwo =:xyz ) "
+ "and STATUS_TX in ('On Hold')")
int countAllByStatusAndName(#Param("xyz") String xyx);
I have written as
Long countByStatusTXAndColumnOnOrColumnTwo (String status, String xyz) . But its not working
I specifically need that or condition between ColumnOne and ColumnTwo.
There seems to be a typo in the method name. ColumnOn instead of ColumnOne. Try Long countByStatusTXAndColumnOneOrColumnTwo (String status, String xyz)
Related
I'm using NamedNativeQueries with SqlResultSetMappings in a Spring Data (JPA Hibernate MySQL) application, and I've been successful with the Pagination, but not with the sorting.
I've tried two forms of queries:
#NamedNativeQuery(
name = "DatasetDetails.unallocatedDetailsInDataset",
resultClass = DatasetDetails.class,
resultSetMapping = "DatasetDetails.detailsForAllocation",
query = "SELECT dd.id, fk_datasets_id, fk_domains_id, fk_sources_id, dom.name AS domain, " +
"src.name AS source " +
"FROM datasets AS d " +
"JOIN datasets_details AS dd ON dd.fk_datasets_id = d.id " +
"JOIN sources AS src ON src.id = dd.fk_sources_id " +
"JOIN domains AS dom ON dom.id = dd.fk_domains_id " +
"WHERE fk_datasets_id = :datasetId " +
"AND dd.id NOT IN (" +
"SELECT fk_datasets_details_id from allocations_datasets_details) \n/* #page */\n"),
and the second is simply using the count notation on a second query instead of using the #page notation.
#NamedNativeQuery(
name = "DatasetDetails.unallocatedDetailsInDataset.count",
resultClass = DatasetDetails.class,
resultSetMapping = "DatasetDetails.detailsForAllocation",
query = "SELECT count(*)
....
Both methods work for pagination, but the sorting is ignored.
Here is the repository:
public interface DatasetDetailsRepository extends PagingAndSortingRepository<DatasetDetails, Long> {
#Query(nativeQuery = true)
List<DatasetDetails> unallocatedDetailsInDataset(#Param("datasetId") long datasetId,
#Param("page") Pageable page);
}
And the pageable gets assembled like this:
Sort sort = Sort.by(Sort.Order.asc(DatasetDetails.DOMAIN), Sort.Order.asc(DatasetDetails.SOURCE));
Pageable page = PageRequest.of(page, limit, sort);
No errors are thrown, but the sorting simply doesn't get done and no ORDER BY is generated.
Explicitly adding something like ORDER BY #{#page} won't compile.
I encountered the same problem, where I had to dynamically filter/sort using a NamedNativeQuery by different columns and directions; apparently the Sorting was ignored. I found this workaround, which is not necessarily nice but it does the job:
For the repository:
List<MyEntity> findMyEntities(
#Param("entityId") long entityId,
#Param("sortColumn") String sortColumn,
#Param("sortDirection") String sortDirection,
Pageable page);
The native queries look like this:
#NamedNativeQueries({
#NamedNativeQuery(name = "MyEntity.findMyEntities",
query = "select e.field1, e.field2, ..." +
" from my_schema.my_entities e" +
" where condition1 and condtition2 ..." +
" order by " +
" CASE WHEN :sortColumn = 'name' and :sortDirection = 'asc' THEN e.name END ASC," +
" CASE WHEN :sortColumn = 'birthdate' and :sortDirection = 'asc' THEN e.birthdate END ASC," +
" CASE WHEN :sortColumn = 'name' and :sortDirection = 'desc' THEN e.name END DESC," +
" CASE WHEN :sortColumn = 'birthdate' and :sortDirection = 'desc' THEN e.birthdate END DESC" +
),
#NamedNativeQuery(name = "MyEntity.findMyEntities.count",
query = "select count(*) from my_schema.my_entities e" +
" where condition1 and condtition2 ..." +
" and :sortColumn = :sortColumn and :sortDirection = :sortDirection"
)
})
Notice in the count query I use the 2 redundant conditions for :sortColumn and :sortDirection, because once specified as #Param in the repository function, you need to use them in the actual query.
When calling the function, in my service I had a boolean which dictates the direction and a string that dictates the sorting column like this:
public Page<MyEntity> serviceFindFunction(Long entityId, String sortColumn, Boolean sortDirection, Integer pageNumber, Integer pageSize) {
String sortDir = (sortDirection) ? 'asc' : 'desc';
Pageable pageable = new PageRequest(pageNumber, pageSize); // Spring Data 1.0 syntax
// for Spring Data 2.0, as you were using, simply:
// Pageable pageable = PageRequest.of(pageNumber, pageSize);
return entityRepository.findMyEntities(entityId, sortColumn, sortDir, pageable)
}
The 2 things that I don't like about this are the redundant usage of the sortColumn and sortDirection params in the count query, and the way I wrote the order by statement. The reasoning for having separate CASE statements is because I had different data types for the columns that I sorted by, and if they are incompatible (e.g. nvarchar and date), the query will fail with the error:
Conversion failed when converting date and/or time from character string
I could also probably nest the conditionals, i.e. first making a case for the direction, the making an inner case for the columns, but my SQL skills only went this far.
Hope this helps! Any feedback or improvements are very welcomed.
I have for loop in my program where I save new objects to database. It looks like
for (String value: readvalue.readValue()) {
Value value= getValueForSomething(something);
System.out.println(value);
valueRepository.save(value);
}
And this fragment of code is executed every 30s and saving to database all values. Some values in database have two same fields and one other. How can I update values in h2 database instead of insert new?
I would suggest that within your for loop, create a method that checks to see if the object already exists in your H2 DB by querying for it using an unique identifier like an id. Use the following example RDB query as a reference:
private static final String PRODUCT_ALREADY_EXISTS_QUERY = "SELECT EXISTS(SELECT 1"
+ " FROM inventory.products "
+ " WHERE 1 = 1"
+ " AND id = :id)";
Then, if the record does exists, then call an update method that utilizes a query to UPDATE using the unique identifier. An RDB example query would be:
private static final String UPDATE_QUERY = "UPDATE inventory.products"
+ " SET (company_id, name, price, type, quantity, created_date, last_modified_date) = "
+ " (:companyId, :productName, :price, :productType, :quantity, :createdDate, :lastModifiedDateTime)"
+ " WHERE id = :id ";
If the record doesn't exist, then just create the record like you are.
JdbcTemplate is returning an empty list when executing the "query" method.
public List<Loan> getLoanDAO(Employee employee, String s) {
final String SQL = "SELECT CTLPCODCIA, CTLPCODSUC, CTLPCODTRA, EMPNOMBRE, EMPAPATERN, EMPAMATERN, CTLPCODPRE, "
+ "CTLPTIPPRE, TIPDESPRE, CTLPMONEDA, CTLPESTADO, CTLPMONTOP, CTLPNROCUO, CTLPCUOTA, FLAGTIPOCUOTA, CTLGLOSA, CTLDIASFR, "
+ "CTLDOCADJ, CTLUSUCREA, CTLFECCREA "
+ "FROM HR_CTLPREC_SS INNER JOIN HR_EMPLEADO ON CTLPCODCIA=EMPCODCIA AND CTLPCODSUC=EMPCODSUC AND CTLPCODTRA=EMPCODTRA "
+ "INNER JOIN HR_TIPPRE ON CTLPCODCIA=TIPCODCIA AND CTLPCODSUC=TIPCODSUC AND CTLPTIPPRE=TIPCODPRE "
+ "WHERE TIPFLGEST = '1' AND TIPSELFSERVICE = '1' "
+ "AND CTLPCODCIA = ? AND CTLPCODSUC = ? AND EMPCODTRAJEF = ? AND CTLPESTADO = ? ";
List<Loan> loans = jdbcTemplate.query(SQL, new Object[] {
employee.getCTLPCODCIA(), employee.getCTLPCODSUC(), employee.getCTLPCODTRA(), s }, loanMapper);
return loans;
}
However, when replacing the "?" with the same parameters used in execution and executing in sqldeveloper, it returns 4 rows. I don't know what is wrong since I've been doing de data access code in the same way for all the other entities.
Problem solved
As stated by #Julian:
JdbcTemplate is a proved spring component used by a huge number of applications so in my opinion it must be a bug in your code.
It was not a problem from JdbcTemplate, neither my code. It was an issue from the IDE. I just build my project from scratch using maven console commands and the code worked as intended.
Thanks folks.
JdbcTemplate is a proved spring component used by a huge number of applications so in my opinion it must be a bug in your code.
Not sure what version of Spring you are using but jdbcTemplate.query would expect a Loan Mapper class as one of its arguments. There is no such a mapper present in your code.
I suggest you put a breakpoint just before the query and inspect the employee fields and see if they match the values you are playing in the sqldeveloper.
One other thing that it attracts my attention is the third one where u have EMPCODTRAJEF = ? in the query definition but you use employee.getCTLPCODTRA() as the argument. Obviously I don't know your data model but should it rather be employee.getEMPCODTRAJEF() or the other way around?
If this won't work double check your arguments.
final String SQL = "SELECT CTLPCODCIA, CTLPCODSUC, CTLPCODTRA, EMPNOMBRE, EMPAPATERN, EMPAMATERN, CTLPCODPRE, "
+ "CTLPTIPPRE, TIPDESPRE, CTLPMONEDA, CTLPESTADO, CTLPMONTOP, CTLPNROCUO, CTLPCUOTA, FLAGTIPOCUOTA, CTLGLOSA, CTLDIASFR, "
+ "CTLDOCADJ, CTLUSUCREA, CTLFECCREA "
+ "FROM HR_CTLPREC_SS INNER JOIN HR_EMPLEADO ON CTLPCODCIA=EMPCODCIA AND CTLPCODSUC=EMPCODSUC AND CTLPCODTRA=EMPCODTRA "
+ "INNER JOIN HR_TIPPRE ON CTLPCODCIA=TIPCODCIA AND CTLPCODSUC=TIPCODSUC AND CTLPTIPPRE=TIPCODPRE "
+ "WHERE CTLPCODCIA=? AND CTLPCODSUC = ? AND EMPCODTRAJEF = ? AND CTLPESTADO = ? "
+ "AND TIPFLGEST='1' AND TIPSELFSERVICE='1'";
Add this to application.properties to debug your query.
logging.level.org.springframework.jdbc.core = TRACE
Using com.couchbase.client, java-client version 2.2.7 I have been unable to get a n1ql secondary index working that uses a parameterized IN clause. See my example index, query, and java code below
Index
CREATE INDEX `indexName` ON `bucketName`(id,docType) USING GSI ;
Query
public static final String COUNT_STATEMENT = "select count(*) as count " +
"from bucketName " +
"where docType = 'docId' " +
"and id IN $ids " +
"and publishTimestamp between $startTime and $endTime";
Code to submit Query
public int getCountForDuration(Long startTime, Long endTime, Collection<String> ids){
List<String> idList = new ArrayList<>(ids);
JsonObject placeHolders = JsonObject.create()
.put("ids", JsonArray.from(idList))
.put("startTime", startTime)
.put("endTime", endTime);
N1qlQuery query = N1qlQuery.parameterized(COUNT_STATEMENT, placeHolders)
N1qlQueryResult result = bucket.query(query);
...
}
Before adding parameterization this secondary Index was correctly being used by my query. Also my query works if I use a primary Index.
My question is this how do I create a secondary index which
will be used by my query.
The first entry in an index (in your case id) cannot be missing. So documents that have a missing id will not be in the index. Therefore if you do not use a field that is already constrained by conditions your index, then you will have to specify that it is not missing to ensure that can go to your secondary index.
e.g. You can query the following index with just type="entityType"
CREATE INDEX `indexName` ON `bucketName`(type) WHERE `type`="entityType"
I resolved this by adding an additional is not missing clause and for some reason this resolved this. The same solution worked in the pass for me. Here is the updated query:
public static final String COUNT_STATEMENT = "select count(*) as count " +
"from bucketName " +
"where id is not missing " +
"and docType = 'docId' " +
"and id IN $ids " +
"and publishTimestamp between $startTime and $endTime";
#Ben Wilde comment -
"The reason the "is missing" is required is because the first entry in
an index (in 'this' case id) cannot be missing. So documents that have
a missing id will not be in the index so if you do not use a field
that is already constrained by conditions set by your index, then you
will have to specify that it is not missing to ensure that can go to
your secondary index"
I have got a working query, which I need to modify by filtering with constant enum value.
Now it looks this way:
public static final String venueQuery =
"select distinct v from package.Venue v "
+ "<some joins here> "
+ "WHERE v.venueType = package.enums.VenueType.VOUCHER_PROVIDER ";
Changing data this way causes
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token
Column definition is like this:
#Enumerated(EnumType.STRING)
#Column(name = "venue_type")
private VenueType venueType;
Enum definition looks this way:
public enum VenueType {
RESTAURANT, BAR, CAFE, FUN_CLUB, VOUCHER_PROVIDER
}
I am sure that other parts of query works fine, because after removing it, no exceptions are thrown.
Are there tricks for setting constant enum value in HQL query?
The preferred way would be to go about adding parameters to the query and pass the enum instance as the parameter value, but if you don't (or can't) make it a parameterized query, you can still do it with String concatenation like this:
public static final String venueQuery =
"select distinct v from package.Venue v "
+ "<some joins here> "
+ "WHERE v.venueType = '" + VenueType.VOUCHER_PROVIDER.name() +"'";
If you want it a compile time constant query String:
public static final String venueQuery =
"select distinct v from package.Venue v "
+ "<some joins here> "
+ "WHERE v.venueType = 'VOUCHER_PROVIDER'";