How to use Regex keyword in Spring Data Repository Method - java

I am currently using spring-data-jpa version 1.9.4.
I have a MySql table with columns project(integer), summary(varchar), and description(varchar).
I have a regex that I would like to use to search the summary and/or description field meaning that if it finds it in summary does not need to apply regex to description.
The repository method I am attempting to use is:
List<Issue> findByProjectAndSummaryOrDescriptionRegex(long project, String regex)
The error I am receiving is:
java.lang.IllegalArgumentException: Unsupported keyword REGEX (1):
[MatchesRegex, Matches, Regex]
It is difficult in my company environment to update/upgrade versions, so if the issue is NOT my syntax but rather the then if someone knows which version now supports 'Regex' for query derivation or where I could find that specific information I would be grateful. I have looked at the Changelog and it appears that 1.9.4 should support but it appears not.
Thanks for your help!
JD
EDIT 1: I am aware of the #Query annotation but have been asked by my lead to only use that as a last resort if I cannot find the correct version which supports keyword REGEX [MatchesRegex, Matches, Regex]

I would recommend using native query (with #Query annotation) if the Spring data syntax does not work, e.g.:
#Query(nativeQuery=true, value="SELECT * FROM table WHERE project = ?1 AND (summary regexp ?2 OR description regexp ?2)")
List<Issue> findByProjectAndSummaryOrDescription(long project, String regex);
Update
If native query is not an option then (a) could you try it with single column and see if that works and (b) could you try by appending regex to both the columns, e.g.:
List<Issue> findByProjectAndDescriptionRegex(long project, String regex);
List<Issue> findByProjectAndSummaryRegexOrDescriptionRegex(long project, String regex, String regex);

In a followup, I discovered by doing some digging that the authoratative list will reside in the org.springframework.data.jpa.repository.query.JpaQueryCreator class. So for future folks that want to know which keywords from the 'Documented' list are ACTUALLY implemented, look inside JpaQueryCreator and you will the keywords supported as case arguments inside a switch!
Hope this helps!
PS - as suspected, REGEX was not supported in my version

try tu use #Query with param nativeQuery = true inside You can use database regexp_like function :
#Query(value = "select t.* from TABLE_NAME t where regexp_like(t.column, ?1)", nativeQuery = true)
Documentation :
https://www.techonthenet.com/oracle/regexp_like.php

Related

How to add multiline Strings in Java?

How to make long queries more readable?
For example I have this one:
String query = "SELECT CASE WHEN EXISTS (SELECT * FROM users WHERE username = 'username' AND user_password = crypt('password', user_password)) THEN 'match' ELSE 'differ' END";
And it's completely unreadable, are there any ways to beautify it?
Since Java 15, you can use text blocks:
String query = """
SELECT CASE
WHEN
EXISTS (
SELECT *
FROM users
WHERE
username = 'username'
AND user_password = crypt('password', user_password)
)
THEN 'match'
ELSE 'differ'
END
""";
In cases when you don't wont to blend SQL and JAVA you can put SQL queries in an .sql file. And get this text when needed.
public class QueryUtil {
static public String getQuery(String fileName) throws IOException {
Path path = Paths.get("src/test/resources//" + fileName + ".sql");
return Files.readAllLines(path).get(0);
}
}
If you can mix SQL and JAVA then starting from JDK15 you can use text blocks for this.
Also you can generates Java code from your database by using JOOQ, it gives many benefits.
Assuming that you can't move to a newer-than-8 version of Java (or even if you can), by far the best solution is to use an ORM. For Java it pretty much comes down to Hibernate, or jOOQ. jOOQ (and possibly Hibernate, I haven't used it so can't say, sorry) allows you to use a fluent programming interface, which is very much in keeping with existing Java code style and patterns.
Another specific advantage of using an ORM is that you can very easily change which DB engine you use without having to change the Java code that you've written beyond changing the SQL dialect in your setup functions. See https://www.jooq.org/javadoc/latest/org.jooq/org/jooq/SQLDialect.html.
You can use JOOQ and get multiple other benefits like type safety, auto-complete, easy mapping and great support.
Have used it for several projects so far and also competition like Kotlin Exposed but always came back to JOOQ.
Move to Java 13+. There are Text Blocks for this.
Or use some ORM library.

Mockrunner(Java) query with Regex

I am using Mockrunner to mock Sql DB for my unit tests. Following is my query:-
"select * from table where userId in (" + userIds + ")"
Now my userIds is state dependent. I don't need my test cases dependent on the arrangement inside the list - userIds. So I don't need exact match but regex matching. I have already enabled regex matching by below code:-
StatementResultSetHandler statementHandler = connection.getStatementResultSetHandler();
usersResult = statementHandler.createResultSet("users");
statementHandler.setUseRegularExpressions(true);
//How to write this regex query?
statementHandler.prepareResultSet("select * from table where userId in .*", campaignsResult);
But as it is noted, I have no idea about the regex syntax supported by Mockrunner.
Edit: I unable to match queries like "Select * from tables" with "Select * from tab .*". So It has to do something with the way I using regex with Mockrunner
There are some helpful examples available here. For instance:
public void testCorrectSQL() throws Exception {
MockResultSet result = getStatementResultSetHandler().createResultSet();
getStatementResultSetHandler().prepareResultSet("select.*isbn,.*quantity.*", result);
List orderList = new ArrayList();
orderList.add("1234567890");
orderList.add("1111111111");
Bookstore.order(getJDBCMockObjectFactory().getMockConnection(), orderList);
verifySQLStatementExecuted("select.*isbn,.*quantity.*\\(isbn='1234567890'.*or.*isbn='1111111111'\\)");
}
From this, I surmise that it's using standard Java regex syntax. In which case, you probably want:
prepareResultSet("select \\* from table where userId in \\(.*\\)", campaignsResult);
...or perhaps more succinctly (and depending upon exactly how fine-grained your tests need to be):
prepareResultSet("select .* from table where userId in .*", campaignsResult);
The main caveat to be aware of when enabling the regex matching is that any literal special characters that you want in your query (such as *, (, and ) literals) need to be escaped in your regex before it will work properly.

using $addToset with java morphia aggregation

I have mongodb aggregation query and it works perfectly in shell.
How can i rewrite this query to use with morphia ?
org.mongodb.morphia.aggregation.Group.addToSet(String field) accepts only one field name but i need to add object to the set.
Query:
......aggregate([
{$group:
{"_id":"$subjectHash",
"authors":{$addToSet:"$fromAddress.address"},
---->> "messageDataSet":{$addToSet:{"sentDate":"$sentDate","messageId":"$_id"}},
"messageCount":{$sum:1}}},
{$sort:{....}},
{$limit:10},
{$skip:0}
])
Java code:
AggregationPipeline aggregationPipeline = myDatastore.createAggregation(Message.class)
.group("subjectHash",
grouping("authors", addToSet("fromAddress.address")),
--------??????------>> grouping("messageDataSet", ???????),
grouping("messageCount", new Accumulator("$sum", 1))
).sort(...)).limit(...).skip(...);
That's currently not supported but if you'll file an issue I'd be happy to include that in an upcoming release.
Thanks for your answer, I can guess that according to source code. :(
I don't want to use spring-data or java-driver directly (for this project) so I changed my document representation.
Added messageDataSet object which contains sentDate and messageId (and some other nested objects) (these values become duplicated in a document which is a bad design).
Aggregation becomes : "messageDataSet":{$addToSet:"$messageDataSet"},
and Java code is: grouping("messageDataSet", addToSet("messageDataSet")),
This works with moprhia. Thanks.

IntelliJ IDEA code inspection: HQL custom dialect & registered functions

My question is about
using registered functions for date/time manipulations in Hibernate Query Language and
IntelliJ IDEA's code inspection for these registered functions in HQL.
I'm using Hibernate 4.2.5 with Java 7, and SQL Server 2008 R2 as the database, and IntelliJ IDEA 12.1.6.
In an HQL query I need to perform the TSQL DATEADD function - or the equivalent HQL date operation. This doesn't seem to exist.
Here's what I'd like to achieve:
update MyTable set startTime = GETDATE(), targetTime = DATEADD(HOUR, allocatedTime, GETDATE()), endTime = null where faultReport.faultReportId = :faultReportId and slaTypeId = :slaTypeId
Searching for answers online has been disappointingly no help, and the most common advice (like the comment seen here: https://stackoverflow.com/a/18150333/2753571) seems to be "don't use date manipulation in hql." I don't see how I can get around performing the operation in the SQL statement in the general case (e.g. when you want to update one column based on the value in another column in multiple rows).
In a similar fashion to the advice in this post: Date operations in HQL, I've subclassed a SQLServerDialect implementation and registered new functions:
registerFunction("get_date", new NoArgSQLFunction("GETDATE", StandardBasicTypes.TIMESTAMP)); // this function is a duplication of "current_timestamp" but is here for testing / illustration
registerFunction("add_hours", new VarArgsSQLFunction(TimestampType.INSTANCE, "DATEADD(HOUR,", ",", ")"));
and added this property to my persistence.xml:
<property name="hibernate.dialect" value="my.project.dialect.SqlServerDialectExtended" />
and then I'm testing with a simple (meaningless, admitted) query like this:
select x, get_date(), add_hours(1, get_date()) from MyTable x
The functions appear to be successfully registered, and that query seems to be working because the following SQL is generated and the results are correct:
select
faultrepor0_.FaultReportSLATrackingId as col_0_0_,
GETDATE() as col_1_0_,
DATEADD(HOUR,
1,
GETDATE()) as col_2_0_,
... etc.
But I now have this problem with IntelliJ IDEA: where get_date() is used in the HQL, the code inspection complains "<expression> expected, got ')'". This is marked as an error and the file is marked in red as a compilation failure.
Can someone can explain how to deal with this, please, or explain what a better approach is? Am I using the incorrect SQLFunction template (VarArgsSQLFunction)? If yes, which is the best one to use?
I'd like the usage of the registered function to not be marked as invalid in my IDE. Ideally, if someone can suggest a better way altogether than creating a new dialect subclass, that would be awesome.

How to use jooq to express mysql REGEXP?

I have query like:
SELECT *
FROM uni_customer
WHERE mobile REGEXP '^(1[3,4,5,8]){1}\\d{9}$'
But there is no such function like REGEXP(String) in field,
and mysql don't support syntax like:
SELECT *
FROM uni_customer
WHERE regexp(mobile,'^(1[3,4,5,8]){1}\\d{9}$')
Official support for the REGEXP operator will be available soon in the upcoming jOOQ 2.5.0. In the mean time, you can extend jOOQ yourself, as such:
Condition regexp = Factory.condition("{0} REGEXP {1}",
UNI_CUSTOMER.MOBILE,
val("^(1[3,4,5,8]){1}\\d{9}$"));
Or in a query:
create.select()
.from(UNI_CUSTOMER)
.where(condition("{0} REGEXP {1}",
UNI_CUSTOMER.MOBILE, val("^(1[3,4,5,8]){1}\\d{9}$")));

Categories

Resources