Using Jooq with jsonb_agg on postgreSQL without code generator? - java

I would like to use Jooq with jsonb_agg in order to create jsonb objects I can read as a stream. Is there a way to do this without having Jooq look at the database?
I know I can have Jooq look at the database and decide what aggregates it finds but is there a way to use this without going through that introspection process? I have been through the docs 4 times and don't see an answer.

So the answer is buried in the DSL documentation, regarding defining fields.
All of the examples before use some variant of:
create.select(field("example1"), field("example2")).from(table(a)...
However you can define your own fields or even inline function definitions.
A field definition could be something like:
Field<Object> example1 = field("example1");
Or if you want to define a type:
Field<String> example2 = field("example2", String.class);
This becomes important when you want to define more complex fields, such as:
Field<String> jsonAry = function("jsonb_agg", String.class, example2);
Jooq really shines at allowing things to be composed like this. You can then:
Fiend<String> fullJson = function("jsonb_build_object", String.class, example1, example2);
Then if you groupBy(example1) you get a nice jsonb_agg object out.

Related

JOOQ SUM in Kotlin with custom types

currently I have a converter to work with value objects in JOOQ with Kotlin.
My database has a table called transactions with a field type decimal(10, 2) called billing _amount and on my Kotlin code I have a simple ValueObject from DDD to wrap it up, called BillingAmount defined as following
data class BillingAmount(val value: BigDecimal)
As for my jooq custom converter I have the following code:
public final TableField<TransactionsRecord, BillingAmount> BILLING_AMOUNT =
createField(
DSL.name("billing_amount"),
org.jooq.impl.SQLDataType.NUMERIC(10, 2).nullable(false),
this,
"",
org.jooq.Converter.ofNullable(
java.math.BigDecimal.class,
okano.dev.jooqtesting.BillingAmount.class,
t -> new okano.dev.jooqtesting.BillingAmount(t),
u -> u.getValue()
)
);
On my repository, I'm just trying to retrieve a sum a billing amounts, but jooq is complaining that BillingAmount doesn't extend a Number class. I know that is a Java generic validation that is preventing my code from working, but there's any way around, except by extending the Number class, for solving this problem? I thought that the converter should be enough, but for sure I'm wrong about this.
Here's the simple query I'm trying to achieve:
// jooq is an instance of DSLContext
return jooq.select(sum(TABLE.BILLING_AMOUNT))
.from(TABLE)
.fetchSingle()
Any thoughts on this question? Thanks in advance.
I assume that problems you're experiencing is just due to Java's type system.
If so, you can simply coerce the field to a different type for a query, like so (admittedly, muddying the query defining code):
BillingAmount sum = jooq
.select(sum(TABLE.BILLING_AMOUNT.coerce(BigDecimal.class)))
.from(TABLE)
.fetchSingle(TABLE.BILLING_AMOUNT);
It's not quite the same type of result as in original query, though, because it materializes BillingAmount directly, and not a Record<BillingAmount> (because after coercion the query return type would be Record<BigDecimal>.
As a workaround, you can always resort to using plain SQL templating in jOOQ
fun alternativeSum(field: Field<*>) = DSL.field("{0}", field.getDataType(), field)

Class for JPA criteria entity with _ appended to className doesn't exist

I'm trying to convert a simple Play/JPA query to use the criteria API. Below isn't even the query I'm trying to convert; this one's even simpler -- just trying to get something to succeed to begin with.
All the examples I've been finding online expect you to be able to use a class that has _ appended to the class name, much like what I've seen hibernate queries do to table name aliases in the generated SQL. However, I can't get my code to compile this way since there is no class: ExtendedHaulTrain_ (there is however ExtendedHaulTrain)
Is there some kind of annotation I need to add to the ExtendedHaulTrain class? Perhaps I have not been reading deeply enough but the examples I've found so far don't address the issue of the class with the underbar appended.
Here's my code that fails to compile on the last line, specifically on ExtendedHaulTrain_
Query query = JPA.em().createQuery("select DISTINCT(x.trnType) from ExtendedHaulTrain x");
List<String> trainTypes = query.getResultList();
//as criteria query
CriteriaBuilder cb = JPA.em().getCriteriaBuilder();
CriteriaQuery<ExtendedHaulTrain> q = cb.createQuery(ExtendedHaulTrain.class);
Root<ExtendedHaulTrain> xhtRoot = q.from(ExtendedHaulTrain.class);
q.select(xhtRoot.get(ExtendedHaulTrain_.trnType)).distinct(true);
Instead of the MetaModel classes(they end with '_') you can always use the attribute name in form of a string as refrence.
q.select(xhtRoot.get("trynType")).distinct(true);
As noted in my comment there is a notion of a meta-model class I'd rather avoid. So below is how I converted my existing query to use the criteria API. Again, this is just to get a success under my belt; I'm probably not going to replace this query. Rather I have another more complex query, for which I intend to use the Criteria API; this was just to get some familiarity with the Criteria API -- there will probably be more questions to follow!
/*
Query query = JPA.em().createQuery("select DISTINCT(x.trnType) from ExtendedHaulTrain x");
List<String> trainTypes = query.getResultList();
*/
CriteriaBuilder cb = JPA.em().getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(ExtendedHaulTrain.class);
Root root = cq.from(ExtendedHaulTrain.class);
cq.select(root.get("trnType")).distinct(true);
List<String> trainTypes = JPA.em().createQuery(cq).getResultList();
I understand that you do not like these meta-models but this is actually a very useful thing, which keeps your code on the safe side of type-safety (believe me, once you begin to write more queries, you will see the advantage). And the advantage is: you can generate them automatically with the so called meta-model generators (which are annotation processing tools). Hibernate has for example something one generator. In Eclipse it is very easy to generate them. Also in Maven it is easy. I recommend to use them.
UPDATE
Type Safety means actually beside not having to write xhtRoot.get("trynType") also that you work with correct join types. Do not forget, that compared to NamedQueries, CriteriaQueries are not checked on deployment. This means, if you remove or use the wrong type in the generic part of a join result (WrongOwner below)
Join<WrongOwner, Address> address = cq.join(Pet_.owners).join(Owner_.addresses);
you will know that on compile time.

Retrieving the value of selectCount in jooq

I have some code that looks like this:
Record record = jooq
.selectCount()
.from(USERS)
.fetchOne();
Currently I'm doing the following to get the count:
Integer count = (Integer) record.getValue(0);
But it seems like there must be a better solution (that's type-safe...since that's the whole point of using jooq). Any suggestions?
Unfortunately, for this particular query, there aren't many "better" ways to typesafely get the count() value. What you could do, to add type-safety, is this:
Field<Integer> f = count();
Integer count = jooq.
.select(f) // Or selectCount(). Replaced it to illustrate the case
.from(USERS)
.fetchOne(f);
The problem is that most of the type information about the projection has been "lost" to the Java compiler, by the time the fetch() methods are "reached". There is no way that a ResultQuery.fetchXXX() method could recover it from the SELECT clause, and produce it to you.
On the jOOQ user group, some users have argued to move the projection into the fetch() methods, entirely, the way C#'s LINQ, or Scala's SLICK do it. This would greatly complicate the expression of more advanced SELECT statements. An more elaborate explanation is documented here.
With jOOQ 3.0, additional record-level typesafety has been introduced. In jOOQ 3.3, it will thus be possible to fetch a single value as such (has been registered as #2246):
<T> T fetchValue(Select<Record1<T>> select);

Equivalent to NEW operator that uses mutators instead of construtor

JPQL queries can return custom result objects with the NEW operator:
SELECT NEW myPackage.MyVO(e.fieldX, e.relationshipX.fieldY)
FROM MyEntity AS e
This is very useful to feed VOs. The problem is, you have to create constructors that exactly match the number of arguments, order and types of your query projection. This starts to get messy when you use a lot of projections for the same VO... Either you have one big constructor in your VO and resort to a lot of NULL literals on your query, or your VO must have a lot of different constructors.
So my question is: Is there a way in JPQL to set result object fields through mutators instead of constructors?
To people with .NET background, I'm looking to a equivalent of LINQ + object initializers.
DataNucleus JPA certainly supports two ways of instantiating result objects using no non-standard annotations or calls, primarily driven by the fact that it also supports JDO and that has the requirement for it :-
Result type with argumented constructor (as you say)
Result type with default constructor, and with setters
Such as
TypedQuery<MyResultType> q = em.createQuery("SELECT x AS field1, y AS field2 FROM ...", MyResultType.class);
where MyResultType has setters "setField1", "setField2".
Short Answer No you can not use mutators in JPQL.
While I do not know LINQ I can not see this getting done without creating mess .
Now I am sure you know that Classes can have mutlple constructors . So why not create constructors where you will not have too feed in null.
Depending upon what you need and which JPA implementation you are using , most providers do provide non standard ways around it .e.g. Hibernate has #formula which in some cases be used instead to using a constructor.
I you are using JPA2 then criteria queries might be a better choice and can take care of these kind of things.
In somecases you might prefer using #PostLoad .
Either way you need to know this converstion in not happening in SQL so you are not really offloading any work to SQL . Which we generally prefer i.e. make SQL do as much work as possible in single hit.
Yes these are my generalizations and concrete solutions or requirements may not fit.

Map database column1, column2, columnN to a collection of elements

In legacy database tables we have numbered columns like C1, C2, C3, C100 or M1, M2, M3, M100.
This columns represent BLOB data.
It is not possible to change anything it this database.
By using JPA Embeddable we map all of the columns to single fields. And then during embedding we override names by using 100 override annotations.
Recently we have switched to Hibernate and I've found things like UserCollectionType and CompositeUserType. But I hadn't found any use cases that are close to mine.
Is it possible to implement some user type by using Hibernate to be able to map a bundle of columns to a collection without additional querying?
Edit:
As you probably noticed the names of columns can differ from table to table. I want to create one type like "LegacyArray" with no need to specify all of the #Columns each time I use this type.
But instead I'd use
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "A"),
#Parameter(name = "size", value = "128")
})
List<Integer> legacyA;
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "B"),
#Parameter(name = "size", value = "64")
})
List<Integer> legacyB;
I can think of a couple of ways that I would do this.
1. Create views for the collection information that simulates a normalized table structure, and map it to Hibernate as a collection:
Assuming your existing table is called primaryentity, I would create a view that's similar to the following:
-- untested SQL...
create view childentity as
(select primaryentity_id, c1 from primaryentity union
select primaryentity_id, c2 from primaryentity union
select primaryentity_id, c3 from primaryentity union
--...
select primaryentity_id, c100 from primaryentity)
Now from Hibernate's perspective, childentity is just a normalized table that has a foreign key to primarykey. Mapping this should be pretty straight forward, and is covered here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/collections.html
The benefits of this approach:
From Hibernate's point of view, the tables are normalized, it's a fairly simple mapping
No updates to your existing tables
The drawbacks:
Data is read-only, I don't think your view can be defined in an updatable manner (I could be wrong)
Requires change to the database, you may need to create lots of views
Alternately, if your DBA won't even let you add a view to the database, or if you need to perform updates:
2. Use Hibernate's dynamic model mapping facility to map your C1, C2, C3 properties to a Map, and have some code you your DAO layer do the appropriate conversation between the Map and the Collection property:
I have never done this myself, but I believe Hibernate does allow you to map tables to HashMaps. I'm not sure how dynamically Hibernate allows you to do this (i.e., Can you get away with simply specifying the table name, and having Hibernate automatically map all the columns?), but it's another way I can think of doing this.
If going with this approach though, be sure to use the data access object pattern, and ensure that the internal implementation (use of HashMaps) is hidden from the client code. Also be sure to check before writing to the database that the size of your collection does not exceed the number of available columns.
The benefits of this approach:
No change to the database at all
Data is updatable
O/R Mapping is relatively simple
The drawbacks:
Lots of plumbing in the DAO layer to map the appropriate types
Uses experimental Hibernate features that may change in the future
Personally, I think that design sounds like it breaks first normal form for relational databases. What happens if you need C101 or M101? Change your schema again? I think it's very intrusive.
If you add Hibernate to the mix it's even worse. Adding C101 or M101 means having to alter your Java objects, your Hibernate mappings, everything.
If you have 1:m relationships with C and M tables, you'd be able handle the cases I just cited by adding additional rows. Your Java objects contain Collection<C> or Collection<M>. Your Hibernate mappings are one-to-many that don't change.
Maybe the reason that you don't see any Hibernate examples to match your case because it's a design that's not recommended.
If you must, maybe you should look at Hibernate Component Mapping.
UPDATE: The fact that this is legacy is duly noted. My point in bringing up first normal form is as much for others who might find this question in the future as it is for the person who posted the question. I would not want to answer the question in such a way that it silently asserted this design as "good".
Pointing out Hibernate component mapping is pertinent because knowing the name of what you're looking for can be the key when you're searching. Hibernate allows an object model to be finer grained than the relational model it maps. You are free to model a denormalized schema (e.g., Name and Address objects as part of a larger Person object). That's just the name they give such a technique. It might help find other examples as well.
Sorry if I'm misunderstanding your problem here, I don't know much about Hibernate. But couldn't you just concatenate during selection from database to get something like what you want?
Like:
SELECT whatever
, C1||C2||C3||C4||...||C100 AS CDATA
, M1||M2||M3||M4||...||M100 AS MDATA
FROM ...
WHERE ...
(Of course, the concatenation operator differs between RDBMSs.)
[EDIT] I suggest to use a CompositeUserType. Here is an example. There is also a good example on page 228f in the book "Java Persistence With Hibernate".
That allows you to handle the many columns as a single object in Java.
The mapping looks like this:
#org.hibernate.annotations.Columns(columns = {
#Column(name="C1"),
#Column(name="C2"),
#Column(name="C3"),
...
})
private List<Integer> c;
Hibernate will load all columns at once during the normal query.
In your case, you must copy the int values from the list into a fixed number of columns in nullSafeSet. Pseudocode:
for (int i=1; i<numColumns; i++)
if (i < list.size())
resultSet.setInt(index+i, list.get(i));
else
resultSet.setNull(index+i, Hibernate.INTEGER.sqlType());
In nullSafeGet you must create a list and stop adding elements when a column is NULL. For additional safety, I suggest to create your own list implementation which doesn't allow to grow beyond the number of columns (inherit from ArrayList and override ensureCapacity()).
[EDIT2] If you don't want to type all the #Column annotations, use a code generator for them. That can be as simple as script which you give a name and a number and it prints #Column(...) to System.out. After the script ran, just cut&paste the data into the source.
The only other solution would be to access the internal Hibernate API to build that information at runtime but that API is internal, so a lot of stuff is private. You can use Java reflection and setAccessible(true) but that code probably won't survive the next update of Hibernate.
You can use UserTypes to map a given number of columns to any type you wish. This could be a collection if (for example) for collections are always bounded in size by a known number of items.
It's been a while (> 3 years) since I used Hibernate so I'm pretty rusty but I recall it being very easy to do; your BespokeUserType class gets passed the ResultSet to hydrate your object from it.
I too have never used Hibernate.
I suggest writing a small program in an interpreted language (such as Python) in which you can execute a string as if it were a command. You could construct a statement which takes the tedious work out of doing what you want to do manually.

Categories

Resources