How to use substring with criteria builder - postgresql

I want to convert string 11.0.3.200 to 0011.0000.0003.0200 for sorting this String descending.
Expression<String> majorVer = cb.function("SUBSTRING_INDEX", String.class, uf.get(sortAttr), cb.literal("."), cb.literal(1)); //11
Expression<String> lpadMajorValue = cb.function("lpad", String.class, majorVer, cb.literal(4), cb.literal("0"));
Expression<String> minorVerBase = cb.function("REPLACE", String.class, uf.get(sortAttr), cb.concat(majorVer, cb.literal(".")), cb.literal(""));//0.3.200
Expression<String> minorVer = cb.function("SUBSTRING_INDEX", String.class, minorVerBase, cb.literal("."), cb.literal(1)); //0
Expression<String> lpadMinorValue = cb.function("lpad", String.class, minorVer, cb.literal(4), cb.literal("0"));
Expression<String> buildVerBase = cb.function("REPLACE", String.class, minorVerBase, cb.concat(minorVer, cb.literal(".")), cb.literal("")); //3.200
Expression<String> buildVer = cb.function("SUBSTRING_INDEX", String.class, buildVerBase, cb.literal("."), cb.literal(1)); //3
Expression<String> lpadBuildValue = cb.function("lpad", String.class, buildVer, cb.literal(4), cb.literal("0"));
Expression<String> revVer = cb.function("REPLACE", String.class, buildVerBase, cb.concat(buildVer, cb.literal(".")), cb.literal("")); //200
Expression<String> lpadRevisionValue = cb.function("lpad", String.class, revVer, cb.literal(4), cb.literal("0"));
Expression<String> lpadValue = cb.concat(lpadMajorValue, cb.concat(lpadMinorValue, cb.concat(lpadBuildValue, lpadRevisionValue)));
orderList.add(cb.desc(lpadValue));
But I am getting an exception:
nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a];
nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause
org.postgresql.util.PSQLException: ERROR: function substring_index(text) does not exist"
Any suggestion if I am doing something wrong with SUBSTRING_INDEX use with criteria build. If SUBSTRING_INDEX is not supported by criteria builder what is the alternative?

Section 4.6.17.2 of the JPA 2.2 specification lists the functions available in JPA and it does not include SUBSTRING_INDEX. I guess it is safe to assume that earlier versions don't include it either. Therefore JPA won't be able to translate it into something database specific.
It also isn't part of Postgres' SQL dialect. So the simple passthrough doesn't work either, as you noticed.
Fortunately, JPA knows about LOCATE which sounds like what you are looking for.
From the specification:
The LOCATE function returns the position of a given string within a string, starting the search at a spec- ified position. It returns the first position at which the string was found as an integer. The first argument is the string to be located; the second argument is the string to be searched; the optional third argument is an integer that represents the string position at which the search is started (by default, the beginning of the string to be searched). The first position in a string is denoted by 1. If the string is not found, 0 is returned.

Related

Is there any difference between initializing with empty values or null value?

Is there any difference between these two part of code? (I am specially using Flutter/Dart but also interested to know about this in any other popular languages like C/C++, Java, JS, Python, etc.)
Code1:
String a = null; (in Flutter: a = null as String; )
List<T> = null; (in Flutter: List<T> = null as List<T>;)
Code 2:
String a = '';
List<T> = [];
With Dart nullsafety your first examples are invalid (assuming you meant to have a variable name on your List). If a variable can contain null the type needs a '?' suffix:
String? a = null;
List<T>? b = null;
But with or without nullsafety or '?', use of the variables in your second example won't result in runtime errors. For example a.trim(): if a is null a runtime error will occur because you're trying to call (null).trim(). In your second example no error because there is a String object to access, even though that String object is empty of characters. Same for your List: b.forEach((e) {}) when b==null a runtime error occurs because there's no Object to find forEach() - i.e. the runtime doesn't know what to do with (null).forEach(). In your second example the forEach() doesn't execute the function because List b is empty, but there's no runtime error because there's an Object to call forEach() on.
In C: NULL for pointers is usually synonymous with 0. The result of trying to access memory address NULL is "undefined" in C, because it could be valid as 0x00 in some instances like embedded systems or low-level system code, but will usually result in your process being terminated (crash).

Comparing two objects with "special" assertions for certain fields with AssertJ

Given the following class...
public class UserAccount {
private Long id;
private String email;
private String activationCode;
private Date createDate;
}
... I need to compare the actual object with an expected object using AssertJ. However the fields id, activationCode and createDate have dynamic value which I can't hard-code into the assertion.
So the following assertion would fail:
assertThat(actualUserAccount).isEqualTo(expectedUserAccount);
Then I found the following which would ignore certain fields:
assertThat(actualUserAccount)
.usingRecursiveComparison()
.ignoringFields("id", "activationCode", "createDate")
.isEqualTo(expectedUserAccount);
But what I'm actually looking for is to assert for the objects being equal with the following special checks on certain fields:
Is there an id with any Long value?
Is there an activationCode with any String value?
Is there an createDate with any Date value?
Or is there no other way than to write one assertion for each field?
You can specify how to compare certain fields with withEqualsForFields so you could write something like:
assertThat(actualUserAccount)
.usingRecursiveComparison()
.withEqualsForFields((id1, id2) -> id1 instanceof Long && id2 instanceof Long, "id")
.isEqualTo(expectedUserAccount);
I'm checking both ids fields since there is no garantee that id1 is the actual id and id2 the expected id.
You could also write a generic method like BiPredicate<A, B> isType(T type) that returns a bipredicate checking both parameters are of the type T (my suggested signature might not work but you get the idea), that would let you write:
assertThat(actualUserAccount)
.usingRecursiveComparison()
.withEqualsForFields(isType(Long.class), "id")
.withEqualsForFields(isType(String.class), "activationCode")
.withEqualsForFields(isType(Date.class), "createDate")
.isEqualTo(expectedUserAccount);
Here's what isType looks like (I haven't tested it though):
<A, B, T extends Class<?>> BiPredicate<A, B> isType(T type) {
return (a, b) -> type.isInstance(a) && type.isInstance(b);
}
Having said that, I would probably not go that way and write additional assertions.
For reference: https://assertj.github.io/doc/#assertj-core-recursive-comparison-comparators
If you want to verify that all the fields have a value you could use hasNoNullFieldsOrProperties, while returns can be used to refine the verification of email (assuming that getters are exposed):
assertThat(actualUserAccount)
.hasNoNullFieldsOrProperties()
.returns(expectedUserAccount.getEmail(), from(UserAccount::getEmail));
If you must enforce the type of the fields, you can chain:
.extracting(UserAccount::getId, UserAccount::getEmail, UserAccount::getActivationCode, UserAccount::getCreateDate)
.hasExactlyElementsOfTypes(Long.class, String.class, String.class, Date.class);
If getters are not available, your original example is probably the only option to verify the value of email:
assertThat(actualUserAccount)
.usingRecursiveComparison()
.ignoringFields("id", "activationCode", "createDate")
.isEqualTo(expectedUserAccount);
But still you can use extracting(String...) instead of extracting(Function...) to enforce the type of the fields:
assertThat(actualUserAccount)
.extracting("id", "email", "activationCode", "createDate")
.hasExactlyElementsOfTypes(Long.class, String.class, String.class, Date.class);
However, these options are not refactoring-friendly.

iText Error: Nullable object must have a value

Using iText7 PDFSweep, C# implementation.
During redaction, iText appears to throw "System.InvalidOperationException: Nullable object must have a value" exception depending on the regular expression used for redaction.
For example, the code below throws exception:
String regex = #"(\s?7-\d\d\d\d)";
CompositeCleanupStrategy strategy = new CompositeCleanupStrategy();
strategy.Add(new RegexBasedCleanupStrategy(regex).SetRedactionColor(ColorConstants.RED));
PdfDocument pdf = new PdfDocument(new PdfReader(srcFile), new PdfWriter(outFile));
PdfAutoSweep autoSweep = new PdfAutoSweep(strategy);
autoSweep.CleanUp(pdf);
pdf.Close();
However, things work OK if I remove the "\s?" from the regex.
This confuses me in 2 regards
Adding \s? to the regex should only make the regex getting more matches
Notwithstanding the above, why would an exception be thrown depending on the result of regex match?
Edit (Adding stack trace):
Error Msg = Nullable object must have a value.
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at iText.Kernel.Pdf.Canvas.Parser.Listener.RegexBasedLocationExtractionStrategy.GetResultantLocations()
at iText.PdfCleanup.Autosweep.CompositeCleanupStrategy.GetResultantLocations()
at iText.PdfCleanup.Autosweep.PdfAutoSweep.GetPdfCleanUpLocations(PdfDocument doc)
at iText.PdfCleanup.Autosweep.PdfAutoSweep.CleanUp(PdfDocument pdfDocument)
at pdf_redactor.Program.Main(String[] args) in G:\prj\pdf_redactor\pdf_redactor\Program.cs:line 77

Remove several elements from Postgres array with jOOQ

I'm trying to use jOOQ to create a function akin to arrayRemove but that allows removing several elements at once from a PostgreSQL column of type uuid[].
So my first attempt was:
private Field<UUID[]> arrayRemoveAll(final Field<UUID[]> field, final Set<UUID> elements) {
return select(field("array_agg(tab.col)", UUID[].class))
.from(unnest(field).as("tab", "col"))
.where(field("tab.col", UUID.class).notIn(elements))
.asField();
}
Which succeeds at removing every requested element, but has the problem of returning null instead of an empty array if I attempt to remove every element.
So I added a coalesce to my code to make it return an empty array:
private Field<UUID[]> arrayRemoveAll(final Field<UUID[]> field, final Set<UUID> elements) {
final Field<UUID[]> newArray = select(field("array_agg(tab.col)", UUID[].class))
.from(unnest(field).as("tab", "col"))
.where(field("tab.col", UUID.class).notIn(elements))
.asField();
return coalesce(newArray, field("{}", UUID[].class));
}
But running this code threw this exception:
org.jooq.exception.DataAccessException: SQL [<<confidential SQL removed>>]
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near ")"
This is the part of the SQL exception it is complaining about (notice the trailing comma and missing 2nd parameter in the coalesce):
coalesce((select array_agg(tab.col)
from unnest("my_schema"."my_table"."my_field") as "tab"("col")
where tab.col not in (?, ?)), )
Is this a bug in jOOQ?
I found that I had a mix of field and val in the code above, changing field("{}", UUID[].class) to val(new UUID[0]) solves the problem.
Also check Lukas Eder's answer about how to solve the issue using field.
So the final code, with generics, looks like this:
private <T> Field<T[]> arrayRemoveAll(final Field<T[]> field, final Set<T> elements, final T[] emptyArray) {
final Field<T[]> newArray = select(field("array_agg(tab.col)"))
.from(unnest(field).as("tab", "col"))
.where(field("tab.col").notIn(elements))
.asField();
return coalesce(newArray, val(emptyArray));
}
And you can use it in your statements like this:
using(configuration)
.update(MY_TABLE)
.set(MY_TABLE.MY_COLUMN,
arrayRemoveAll(MY_TABLE.MY_COLUMN, someElements, new UUID[0]))
.where(MY_TABLE.ID.eq(...))
.execute();
Your field("{}") does not generate the {} string in the SQL, but is considered a part of jOOQ's plain SQL templating language, which unfortunately doesn't allow for escaping those braces:
https://www.jooq.org/doc/latest/manual/sql-building/plain-sql-templating
Luckily, PostgreSQL supports a more formal, standards-compliant way to create an empty array literal:
field("array[]::uuid[]", UUID.class)

JPA Convert List<Tuple> to List<myClass>

I have the following Criteria API code which returns List.
I would like to convert this to List<myClass>
How can I do this?
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<ProductCatalogue> pc = cq.from(ProductCatalogue.class);
Root<ProductList> al = cq.from(ProductList.class);
.......
.......
.......
Predicate[] predicates = new Predicate[predicateList.size()];
predicateList.toArray(predicates);
criteriaQuery.where(predicates);
TypedQuery<Tuple> typedQuery = getEntityManager().cq(criteriaQuery);
List<Tuple> tuple = typedQuery.getResultList();
Ideally I would like to
List<Employee> emp = tuple
However the above resulted in incompatible type error, I do not know how could I cast this.
If you insist on using a tuple query you'll have to convert the instances manually.
If myClass is an entity you should use CriteriaQuery< myClass> as perissf suggested, otherwise you may use a "constructor expression" to create instances directly from a select, for example:
select new com...myClass(c.id, c.price) FROM ProductList c WHERE c.....;
See this answer for an example to create such a query using the Criteria API.