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));
Related
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);
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.
I'm using jOOQ with Postgresql to select an enum value from a table.
List<my.project.jooq.enums.Color> colors =
dsl.selectDistinct(TABLE.T_COLOR.as("color"))
.from(TABLE).fetch()
.into(my.project.jooq.enums.Color.class);
Anyway I get the exception:
org.jooq.exception.MappingException: No matching constructor found on type class my.project.jooq.enums.Color for record org.jooq.impl.DefaultRecordMapper#7c66447f
I see that fetch() will return Result<Record1<my.project.model.jooq.enums.Color>>, so I wonder if there is a way to immediately fetch the Color enums into a list as I can do with any pojo.
How can I fetch into the enum values?
As of jOOQ 3.7, this is not yet supported out of the box, but will be in jOOQ 3.8 (see #5154).
You can easily map a string column to your Enum type yourself, though, via
List<my.project.jooq.enums.Color> colors =
dsl.selectDistinct(TABLE.T_COLOR)
.from(TABLE)
.fetch()
.map(rec -> my.project.jooq.enums.Color.valueOf(rec.getValue(TABLE.T_COLOR)));
In case, your my.project.jooq.enums.Color was generated by jOOQ from a PostgreSQL enum data type, you don't need to specifically map anything, jOOQ will automatically do that for you:
List<my.project.jooq.enums.Color> colors =
dsl.selectDistinct(TABLE.T_COLOR)
.from(TABLE)
.fetch(TABLE.T_COLOR);
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
Is there a posibility to define with JPA different column types depending on the used database?
I need to store the id as uuid and it must be protable. That's the problem. PostgreSQL has 'uuid', MSSQL 'uniqueidentifier' and Oracle has nothing, there must be used 'RAW', I think.
Have anybody an idea and can help me?
Edit: Currently the ids are generated with java.util.UUID and stored in the database as varchar. But because of performance problems with that data type I want to store the ids as a uuid type.
For Oracle must use the RAW type because no uuid type exist. How can I tell JPA use uuid type with PostgreSQ/MSSQL and RAW type with Oracle?
You can use java.util.UUID to generate your uuids and store it into a character varying(60) column type.
Now since you want to be a primary key it should be good to auto generate it a persist. So you can add on your class an entity listener at PrePersist:
#EntityListeners({ AbstractEntity.AbstractEntityListener.class})
public class Entity {
public static class AbstractEntityListener {
#PrePersist
public void onPrePersist(Entity entity) {
entity.uid();
}
}
private String uid() {
if (uid == null)
uid = UUID.randomUUID().toString();
return uid;
}
}
Note that i didn't test this. So it may not work 100%.
I would use a SequenceGenerator. Check this answer for more details. You will have to make one seq. generator per entity in order to make sure you do not get concurrency problems.
Also make sure that you set a good/not existent initial value.