jooq: How to configure dialect for static DSL methods? - java

I've got dsl with POSTGRES_9_4 dialect. I try to use custom selection query with org.jooq.impl.DSL.Condition:
dsl.selectFrom(TAG_JSON).where(condition("translations ??| array[?]", normValues)).fetch(mapper);
It throws an exception:
org.jooq.exception.SQLDialectNotSupportedException: Type class java.util.ArrayList is not supported in dialect DEFAULT
at org.jooq.impl.DefaultDataType.getDataType(DefaultDataType.java:757)
at org.jooq.impl.DefaultDataType.getDataType(DefaultDataType.java:704)
at org.jooq.impl.DSL.getDataType(DSL.java:14371)
at org.jooq.impl.Utils.queryParts(Utils.java:1565)
at org.jooq.impl.SQLImpl.<init>(SQLImpl.java:64)
at org.jooq.impl.DSL.sql(DSL.java:6240)
at org.jooq.impl.DSL.condition(DSL.java:7323)
Why DEFAULT dialect is used? How to configure global one?

The error message is a bit misleading. Your intention seems for array[?] to take a List<String> as a single bind value to pass to an array constructor. This won't work. You have two options:
Binding a single array
condition("translations <op> ?::text[]", normValues.toArray(new String[0]))
Binding the array as a list of bind values
condition("translations <op> {0}", list(
normValues.stream().map(DSL::val).toArray(QueryPart[]::new)
))
This is using DSL.list() to create a list of bind variables.
Note, in both cases, I have avoided your ??| operator because jOOQ parses plain SQL text for bind values (?) and currently doesn't recognise ??| as a non-bind variable sequence (I've registered an issue for this). If that operator has a textual alternative representation, I recommend using that instead.

Related

Automatic data type conversion in jOOQ's code generation

I have the following questions:
eg:
Does jooq provide a conversion mechanism like mybatis type, rather than every time I need to manually convert. For example, int is converted to Byte. Long [] into UInteger and so on. I do not know how to deal with the type of conversion, can give me a detailed solution.
The code generation tool is as follows:
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration()
.withJdbc(new Jdbc()
.withDriver("com.mysql.jdbc.Driver")
.withUrl("jdbc:mysql://localhost:3306/51unboxdb")
.withUser("root")
.withPassword("root")
)
.withGenerator(
new Generator()
.withName("org.jooq.util.JavaGenerator")
.withGenerate(new Generate()
.withPojos(true)
.withImmutablePojos(true)
.withInterfaces(true)
.withDaos(true)
.withSpringAnnotations(true)
.withJavaTimeTypes(true)
)
.withDatabase(new Database()
.withName("org.jooq.util.mysql.MySQLDatabase")
.withIncludes(".*")
.withExcludes("")
.withDateAsTimestamp(true)
.withInputSchema("51unboxdb")
)
.withTarget(new Target()
.withPackageName("com.chunfytseng.unbox.jooq")
.withDirectory("src/main/java")
)
);
GenerationTool.generate(configuration);
}
There are many tables in the database. When you modify a property of a table and then overwrite the existing code from the newly generated code, it makes me very troublesome. Can I specify to update a table or exclude some tables? Not every time you build it overwrites the existing code.
There are at least two ways to tackle this problem generically
1. By using the code generator's type rewriting feature
The code generator supports a feature called type rewriting, where you can specify a regular expression matching all relevant column names and/or data types, whose generated type should be rewritten to BIGINT in your case (as you'd like to use long rather than UInteger. Just add:
...
.withDatabase(new Database()
...
.withForcedTypes(new ForcedType()
.withName("BIGINT")
.withExpression("(?i:.*\.USER\.ID)")
)
...
)
...
2. By using the built-in auto-conversion API
jOOQ's auto-conversion utility is called org.jooq.tools.Convert. It already covers a vast variety of automatic type conversions, e.g. between different well-known numeric types as you’ve shown. There’s also a lot of convenience API, e.g. you can pass a Class reference of your desired type to the fetch() methods in order to get a result of such type.

How to use Hibernate 5.2.10 MySQL JSON support without AttributeConverter or customUserType to map to Java Entity Class?

I am trying to map the MySQL JSON column to Java Entity class. Looking for the cleanest way of doing this.
Upon doing some research found 3 possible ways:
Extending AbstractSingleColumnStandardBasicType
Create a custom UserType
Use an attribute Converter
I used an attribute converter to convert the JSON column from String (as MySQL driver makes it to a String) to my required type - this works with both the Hibernate V4.3.10 and V5.2.10
I tried to find if JSON is natively supported in Hibernate and found the PR https://github.com/hibernate/hibernate-orm/pull/1395, based on the PR looks like it does add JSON mapping to the MySQL Dialect hence letting Hibernate know about the JSON Column.
Does this mean I can use something like this to map to JSON Column in DB ?#Column(name="json_type_column")
Private Object correspondingJsonAttribute;
If I cannot use it like this and need to use one of the above 3 methods, is there a reason I would need to upgrade to get the registerColumnType( Types.JAVA_OBJECT, "json" ); which is part of the PR and is present in Hibernate V5.2.10, Do I get any more features from V5.2.10 that support JSON columns?
I also looked into the corresponding test case to understand how the JSON column mapping is being done https://github.com/hibernate/hibernate-orm/blob/master/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/access/MixedAccessTestTask.java, this uses #Access annotation via property, looks like it sets the corresponding JSON column variable in Entity to Map after converting it from String.
Any help is much appreciated.
Thanks!
Upon doing some research found 3 possible ways:
Extending AbstractSingleColumnStandardBasicType
Create a custom UserType
Use an attribute Converter
AttributeConvertor won't help you for this, but you can still use a custom UserType, or Hibernate Type Descriptors.
Does this mean I can use something like this to map to JSON Column in
DB?
#Column(name="json_type_column") Private Object
correspondingJsonAttribute;
No. The json type is just for JDBC so that Hibernate knows how to handle that JDBC object when setting a parameter on a PreparedStatement or when fetching a ResultSet.
Do I get any more features from V5.2.10 that support JSON columns?
No, but you just need to supply your own JSON type.
You can just use the hibernate-types which is available on Maven Central.
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>${hibernate-types.version}</version>
</dependency>
And use the provided JdonType from Hibernate Types as it works on MySQL, PostgreSQL, Oracle, SQL Server or H2 without doing any modifications.

jOOQ .fetchMap() with Converters

I am trying to execute .fetchMap(key, value) with jOOQ but I want to process the key through a custom converter.
The docs are very clear on how to use converters and how to use .fetchMap() but I can't find anywhere a way of combining both.
Could this feature be missing from my jOOQ version (3.9) ?
Converter (and Binding) implementations are bound to the Field reference by the code generator, or you can do it manually like this:
// Using this static import
import static org.jooq.impl.DSL.*;
// Assuming a VARCHAR column in the database:
DataType<MyType> type = SQLDataType.VARCHAR.asConvertedDataType(
new MyConverter<String, MyType>());
Field<MyType> field = field(name("MY_TABLE", "MY_FIELD"), type);
Now, whenever you fetch this field in your SELECT statements, e.g.
Result<Record1<MyType>> result =
DSL.using(configuration)
.select(field)
.from(...)
.fetch();
jOOQ will automatically apply your converter while fetching results from the underlying JDBC ResultSet. You will never see the original String value in your result.
The ResultQuery.fetchMap(Field, Field) method that you've mentioned is just short for fetch() and then Result.intoMap(Field, Field). In other words, the converter will already have been applied automatically by the time you call fetchMap() or intoMap(), so there is no need to do anything specific. Just use your field as an argument to fetchMap() :
Map<MyType, OtherType> result =
DSL.using(configuration)
.select(field, otherField)
.from(...)
.fetchMap(field, otherField);

jOOQ : compare uuid(from postgresql) and string(user id in class)

My backend is postgresql. I am trying to write a simple function to fetch data from database, using jooq records. The DAO for this table is written by me. The problem I am facing is comparison of UUID in db and String provided to function.
public Correspondence fetchByExternalId(String externalId) {
CorrespondenceRecord correspondenceRecord =
create.fetchOne(Tables.CORRESPONDENCE,
Tables.CORRESPONDENCE.USERID.eq(externalId));
CORRESPONDENCE.USERID is UUID and externalId is String; The operator eq is the one which i am not able to implement here, which has worked for me on previous occasions.
The error shown is :
Cannot resolve method 'eq(java.lang.String)'
when i referred to the jOOQ site for help it show 'eq' is the operator it suggests to be used!
Can anyone help me with an alternative or tell me where i went wrong?
jOOQ is a very type safe API, so you cannot compare UUID types with String types using eq(), because the eq() method uses the generic <T> type of your CORRESPONDENCE.USERID column, which is UUID:
TableField<..., java.util.UUID> USERID = ...
Generating a UUID bind value in the Java client
You either have to supply an externalId in the form of a UUID type, e.g.:
CORRESPONDENCE.USERID.eq(UUID.fromString(externalId))
... or, you let jOOQ convert that type for you:
CORRESPONDENCE.USERID.eq(CORRESPONDENCE.USERID.getDataType().convert(externalId))
Both of the above are equivalent.
Generating a UUID bind value in the database
You can always also defer the type conversion work to the database by casting (which will result in a CAST(? AS UUID) being rendered:
CORRESPONDENCE.USERID.eq(DSL.cast(externalId, UUID.class));
... or by coercing the variable to adhere to the UUID type (trusting that the database can implicitly convert the VARCHAR type to a UUID type):
CORRESPONDENCE.USERID.eq(DSL.coerce(DSL.val(externalId), UUID.class));

Java- PLSQL- Call Table of records from java

PLSQL package-procedure declarations
TYPE custom_type IS TABLE OF single_rec_type;
TYPE single_rec_type IS RECORD(
//id, name etc
)
Problem:
But custom_type has no direct Java type representation [like OracleTypes.CLOB or OracleTypes.CURSOR]
because custom_type is a PLSQL type and not a SQL type.
When I googled, I came across these two options:
To represent it ,
(1) create a SQL TYPE from procedure(or a wrapper PLSQL function) that we can bind from java.
Reference: java - passing array in oracle stored procedure
(2) Register the output parameter with our type and use SQLData object to represent a record.
Reference: Howto get a table as a out parameter in oracle
callableStatement.registerOutParameter(8, OracleTypes.ARRAY, "custom_type");
On doing this, I get the error:
java.sql.SQLException: invalid name pattern: MYDB_OWNER.custom_type
at oracle.jdbc.oracore.OracleTypeADT.initMetadata(OracleTypeADT.java:554)
at oracle.jdbc.oracore.OracleTypeADT.init(OracleTypeADT.java:471)
One suggestion was to declare the custom_type TYPE inside the schema, instead of declaring inside the package.
or by creating public synonym and giving grants.
Question - Regarding the second approach, is it correct practice to declare any custom type in schema level?
Yes, That's the only way it works. I followed the link mentioned in second approach
Howto get a table as a out parameter in oracle
and it worked. The Package level changes included (1) Declaring the custom_type and single_rec_type in schema level [as global, not inside the package] and (2) Replacing IS RECORD with AS OBJECT.
The Java code changes apart from what was mentioned in the link, includes giving the complete name for the class in map.put("SINGLE_REC_TYPE", Class.forName("com.example.SRecord"));
Another thing to notice is that in that example, it mentioned stream.readString();. If you read the API, it says 'Reads the next attribute in the stream and returns it as a String in the Java programming language.' . So if you have three attributes inside the object, then use the method three times like this
id = stream.readString();
name = stream.readString();
designation = stream.readString();
Another point is well mentioned in that post; Regarding the datatypes of attributes inside the object. If there are type mismatch, you get internal representation errors.
eg: correct way:
SRecord.java
public String id; \\varchar in plsql procedure
public String name; \\varchar in plsql procedure

Categories

Resources