I'm currently trying to introduce this function https://stackoverflow.com/a/12257917/4375998 into my project, but struggle with the function signature generated by jooq.
The generated code has these three signatures
public static String udfNaturalsortformat(
Configuration configuration
, String instring
, Integer numberlength
, String sameorderchars
)
public static Field<String> udfNaturalsortformat(
String instring
, Integer numberlength
, String sameorderchars
)
public static Field<String> udfNaturalsortformat(
Field<String> instring
, Field<Integer> numberlength
, Field<String> sameorderchars
)
But none of them seem to fit my use case of ordering a table by name.
This is currently done in the following fashion: In some place a collection of search parameters is generated and this is later included in the query:
public Collection<? extends SortField<?>> getSortFields(List<Pageable.SortField> sorts) {
if (sorts.isEmpty()) {
return Collections.singletonList(ProjectRepositoryImpl.LAST_ACTIVITY.field("last_activity").desc());
}
List<SortField<?>> sortFields = new ArrayList<>();
for (Pageable.SortField sort : sorts) {
if (sort.getSortDirection() == Pageable.SortDirection.DESC) {
sortFields.add(PROJECT.NAME.desc());
} else {
sortFields.add(PROJECT.NAME.asc());
}
}
return sortFields;
}
And the final query then looks like this
Result<Record> queryResults = jooq.select(PROJECT.fields())
.from(PROJECT)
.where(searchCondition)
.orderBy(transformer.getSortFields(pageable.getSorts()))
.limit(pageable.getPageSize())
.offset(pageable.getOffset())
.fetch();
So what I currently attempt to do is replace the sort field with something like this
sortFields.add(udfNaturalsortformat(PROJECT.NAME, 10, ".").desc());
but the signature mismatches.
What is the proper way to include this method in my order by statement?
As you can see in signatures of generated methods:
public static Field<String> udfNaturalsortformat(
String instring
, Integer numberlength
, String sameorderchars
)
public static Field<String> udfNaturalsortformat(
Field<String> instring
, Field<Integer> numberlength
, Field<String> sameorderchars
)
It has only overrides for all java simple objects (first one) or Field<?> references (second one).
Since you use PROJECT.NAME as a first argument, you would probably use second generated method, but then you have to pass other arguments all of Field<?> type. So try DSL.val or DSL.inline, which is a way to pass constant value as a field:
udfNaturalsortformat(Staff.STAFF.ACQUIRED_COMPANY, DSL.val(10), DSL.val(".")).desc();
Related
Is there a way to avoid calling this.field for every field in a class ?
public class Test {
private String name;
private String email;
public Test(String name, String email) {
// I want to avoid this
this.name = name;
this.email = email;
}
public Test(Test test) {
// Something like this would be perfect, setting both name and email to test
this(test);
}
}
The use of this is only required in cases of name collisions, to resolve the ambiguity.
Some programmers like me prefer using the this. prefix routinely, whereas other use only where necessary.
See Answer by Wasserman for an example of how to avoid naming collision.
Use the IDE, Luke
Your IDE will generate constructors, accessors (getters/setters), equals & hashCode, toString, and so on. So you need not type this.; let the machine do the typing.
Use custom settings to control whether you want the IDE to include or omit this. prefixes.
record
You may be interested in using the records feature, new in Java 16+. A record is a brief way to write a class whose main purpose is to communicate data transparently and immutably.
With a record, by default, the compiler implicitly writes the constructor, getters, equals & hashCode, and toString. The implicitly created constructor populates each and every member field on your behalf. You write none of that code.
Here is your entire example class when written as a record. No this required. All your member fields are automatically assigned.
public record Test ( String name , String email ) {}
Be cautious in using records. The reason for their invention was not writing less code. The reason was to provide an explicit mechanism for transmitting immutable data transparently, a “nominal tuple” in academic-speak. Less boilerplate coding is merely a nice side-effect. I highly recommend reading JEP 395 for more explanation.
Tip: You can combine the two points of this Answer. Ask your IDE to generate a full-blown class by starting with a record.
Write a record with all your member fields listed in the parentheses.
Invoke your IDE to convert from a record to a class.
Voilà, you have a complete class with constructor, accessors, equals & hashCode, and toString all written out with an absolute minimum of typing by you.
For example, in IntelliJ 2022, choosing Convert record to class from the light-bulb icon menu turns this:
public record Test ( String name , String email ) {}
… into this:
package work.basil.example.recs;
import java.util.Objects;
public final class Test
{
private final String name;
private final String email;
public Test ( String name , String email )
{
this.name = name;
this.email = email;
}
public String name ( ) { return name; }
public String email ( ) { return email; }
#Override
public boolean equals ( Object obj )
{
if ( obj == this ) { return true; }
if ( obj == null || obj.getClass() != this.getClass() ) { return false; }
var that = ( Test ) obj;
return Objects.equals( this.name , that.name ) &&
Objects.equals( this.email , that.email );
}
#Override
public int hashCode ( )
{
return Objects.hash( name , email );
}
#Override
public String toString ( )
{
return "Test[" +
"name=" + name + ", " +
"email=" + email + ']';
}
}
Caveat: That result may not be the default. I may have altered the settings in IntelliJ.
Sorry, the only way to avoid this is to have different names for your constructor parameters and for your class fields.
public Test(String _name, String _email) {
// I want to avoid this
name = _name;
email = _email;
}
That said, you might have better luck using Java 16+'s record syntax.
As suggested, using records is the easiest way:
public record Test (String name, String email) {
}
That's all you need. What you then get:
A constructor that takes all arguments, in the same order as the field list
A method for each field. This does not start with get. In this case, the methods are name() and email().
equals, hashCode and toString implementations that use all fields.
There is no need for a copy constructor, because every field is automatically final.
If you want, you can add extra constructors. However, they must delegate to the automatically generated constructor, because that's the one that sets the fields. Adding additional utility methods is also fine.
And if needed, you can add validation to the generated constructor. There's special syntax that allows you to omit all the field names:
public record Test (String name, String email) {
public Test {
Objects.requireNonNull(name);
Objects.requireNonNull(email);
}
}
The assignments are done for you, there's no need to type those either.
You need this.x everytime, if there are 2 or more variables, which are called x and you want to call the attribute variable x.
The this keyword is used, to point on an attribute variable of the created instance (object) of the class.
There could be an attribute, that is called x, and a local variable which is called x too.
im using ObservableList in my JavaFX code , i'm trying to test if an object exists in this list but it always returns false even for those already existing in it .
here is my code :
private ObservableList<OMission> UserMission = FXCollections.observableArrayList();
OMission OM1 = new OMission(1,"user_firstname","user_lastname");
UserMission.add(OM1);
if(UserMission.contains(new OMission(1,"user_firstname","user_lastname")){
System.out.println("true");}
else {
System.out.println("false");
}
I was expecting to get "true" but im always getting false
what's happening ?
You likely neglected to implement the crucial Object overrides for equals and hashCode.
This issue is not specific to the OpenJFX class ObservableList. Any collection (list, set, map, etc.) that performs comparisons will depend on you writing an appropriate override for at least equals & possibly hashCode too. (Tip: Always override both or neither, never one alone.)
Below is example code using a modified version of your code. By the way, you were missing a right-paren. Also, life is easier if you follow the Java naming conventions.
For brevity, we use the new records feature in Java 16+. A record is a briefer way to write a class whose main purpose is to communicate data transparently and immutably. You merely need to declare the type and name of each member field. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString. Those last three methods by default examine each and every member field‘s value.
For simplicity we declared the record locally. You can just as well declare it nested or separate.
package work.basil.example;
import javafx.collections.*;
public class App {
public static void main ( String[] args ) {
System.out.println ( "Hello World!" );
App app = new App ();
app.demo ();
}
private void demo () {
record Person( int id , String firstName , String lastName ) { }
ObservableList < Person > UserMission = FXCollections.observableArrayList ();
Person p1 = new Person ( 1 , "Alice" , "Anderson" );
UserMission.add ( p1 );
if ( UserMission.contains ( new Person ( 1, "Alice" , "Anderson" ) ) ) {
System.out.println ( "true" );
} else {
System.out.println ( "false" );
}
}
}
When run.
Hello World!
true
If working with earlier versions for Java, or if a record is not appropriate to your situation, write a class similar to the following. Notice the methods equals & hashCode.
package work.basil.example;
import java.util.Objects;
public final class Person {
private final int id;
private final String firstName;
private final String lastName;
public Person ( int id , String firstName , String lastName ) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
public int id () { return id; }
public String firstName () { return firstName; }
public String lastName () { return lastName; }
#Override
public boolean equals ( Object obj ) {
if ( obj == this ) return true;
if ( obj == null || obj.getClass () != this.getClass () ) return false;
var that = ( Person ) obj;
return this.id == that.id &&
Objects.equals ( this.firstName , that.firstName ) &&
Objects.equals ( this.lastName , that.lastName );
}
#Override
public int hashCode () {
return Objects.hash ( id , firstName , lastName );
}
#Override
public String toString () {
return "Person[" +
"id=" + id + ", " +
"firstName=" + firstName + ", " +
"lastName=" + lastName + ']';
}
}
The issue of overriding equals/hashCode has been covered many many times already. Search to learn more.
Basil concurrently updated his answer to include some additional info while I was writing this. So, some info is duplicated in this answer. Please forgive any duplication here. I'll just leave this answer as-is for now.
To supplement Basil's answer, some different solutions are:
Write custom implementations of equals and hashcode.
For OMission, implement equals(Object obj) (and probably best to have hashCode() implemented too).
You will end up with code like in Basil's example answer.
You can code these methods yourself from scratch, however, I don't recommend that. It is too easy to make needless, silly mistakes when coding these functions by hand.
Use an IDE to auto-generate implementations of equals and hashcode.
Most IDEs have a menu item or shortcut to autogenerate these methods (see how to do this in Idea). This is how I normally generate these functions. If I add or remove a field to the class, then I delete the previous auto-generated functions and auto-generate new ones.
Use a 3rd party library to generate equals and hashcode.
A third-party library, such as Lombok, can be used to autogenerate the methods. Lombok will do this via annotations, e.g. just add the annotation #EqualsAndHashcode to your class definition.
Make OMission a record instead of a class.
If it is appropriate that the data can be represented as an immutable record rather than a class, then this is a recommended approach. It will not be appropriate for all data types.
By default, records implement appropriate equals and hashcode methods.
record OMission(
int missionNum,
String firstname,
String lastname
) {}
I am working in with a database in which dates are stored as unix time (seconds since 1970). I have the following sql which works as expected:
select CONVERT_TZ(FROM_UNIXTIME(creation_date), ##session.time_zone, "Europe/Berlin")
from transaction;
This is how I tried to do it in Jooq:
dsl.select(DSL.date(TRANSACTION.CREATION_DATE) // This does not work
.from(TRANSACTION)
.fetch();
You're using quite a few vendor specific functions there, which are not supported out of the box in jOOQ. As always, when you run into this situation, you can roll your own support by using plain SQL templating, ideally by building your own library:
public static Field<Timestamp> fromUnixtime(Field<? extends Number> field) {
return field("from_unixtime({0})", SQLDataType.TIMESTAMP, field);
}
public static Field<Timestamp> convertTz(
Field<Timestamp> field,
Field<String> fromTz,
Field<String> toTz
) {
return field("convert_tz({0}, {1}, {2})", SQLDataType.TIMESTAMP, field, fromTz, toTz);
}
public static Field<String> sessionTimeZone() {
return field("##session.time_zone", SQLDataType.VARCHAR);
}
Now, you can use it like this:
dsl.select(convertTz(
fromUnixtime(TRANSACTION.CREATION_DATE),
sessionTimeZone(),
inline("Europe/Berlin"))
)
.from(TRANSACTION)
.fetch();
Or, take it a step further and wrap all of these expressions in yet another auxiliary function, like this one:
public static Field<Timestamp> fromUnixtimeToBerlinTZ(Field<? extends Number> field) {
return convertTz(
fromUnixtime(TRANSACTION.CREATION_DATE),
sessionTimeZone(),
inline("Europe/Berlin")
);
}
All of these examples are assuming the usual static import:
import static org.jooq.impl.DSL.*;
I am trying to convert something like this in jOOQ:
select foo from bar
order by field(foo, 'value1', 'something-else', 'value3')
on a select query, like:
SelectQuery<Record> query = ...
query.addSelect(BAR.FOO);
query.addFrom(BAR);
query.addOrderBy( ... ? ... )
How does one add the last bit?
Background
What I am trying to accomplish is basically described here: MySQL - ORDER BY values within IN(). In my case, I have a generic batch load function that uses 'where field in(..)' and I want to preserve order. This works as I need it to using plain SQL, but I need to add this bit to a dynamically constructed query with jOOQ.
Whenever you hit jOOQ's limits, resort to plain SQL. You can write your own field function like this:
class MyDSL {
public static Field<Integer> field(Field<String> search, String in1) {
return field(search, DSL.val(in1));
}
public static Field<Integer> field(Field<String> search, Field<String> in1) {
return DSL.field("field({0}, {1})", Integer.class, search, in1);
}
public static Field<Integer> field(Field<String> search,
String in1,
String in2) {
return field(search, val(in1), val(in2));
}
public static Field<Integer> field(Field<String> search,
Field<String> in1,
Field<String> in2) {
return DSL.field("field({0}, {1}, {2})", Integer.class, search, in1, in2);
}
// ... or, support a varargs function variant, too
}
And now use that in all your statements:
query.addOrderBy( MyDSL.field(BAR.FOO, "value1", "something-else", "value3") );
This seems to do the trick. Not sure if there is a better answer,
Field[] args = new Field[]{DSL.field("foo"),
DSL.val("value1"), DSL.val("something-else"), DSL.val("value3")}
query.addOrderBy(DSL.function("field", SQLDataType.INTEGER, args));
You can use something like to convert following sql to jooq. Here 'sortAsc' is used to sort according to the given value order.
SQL
select foo from bar order by field(foo, 'value1', 'something-else', 'value3')
JOOQ
DSL()
.select(BAR.FOO)
.from(BAR)
.orderBy(BAR.FOO.sortAsc('value11', 'something-else', 'value3'))
.fetch()
In the table is a pk with uuid's stored as binary(16).
I'm able to retrieve the hexadecimal using plain sql:
select hex(UUID) as uuid from tbl ;
But the jooq equivalent does not know a hex function.
Result<Record1<byte[]>> result = ctx
.select(tbl.UUID)
.from(tbl)
.fetch();
Casting to String gives the id of the java object.
Any idea's?
Result<Record1<byte[]>> result = ctx
.select(tbl.UUID.cast(String.class))
.from(tbl)
.fetch();
Same problem using ip's (ipv4, ipv6):
select inet_ntoa(conv(hex(IP), 16, 10)) as ip from tbl ;
jOOQ doesn't support all vendor-specific functions out of the box. Whenever you are missing such a function, you can create it yourself using plain SQL:
public class DSLExtensions {
public static Field<String> hex(Field<byte[]> field) {
return DSL.field("hex({0})", String.class, field);
}
}
This obviously holds true for your other functions, too, such as inet_ntoa() and conv()
#Lukas Eder, you can simplify that even more.
public static Field<String> hex(Field<byte[]> field) {
return DSL.function("hex", String.class, field);
}