JOOQ multi-field custom type converter - java

We have some custom types that reflected to multiple db fields. For example
PersonName{
String salutation,
String firstName,
String lastName
}
stored as 3 separate db fields.
And it's boring to always write
db.select(PERSON.FIRST_NAME, PERSON.LAST_NAME, PERSON.SALUTATION, ... some other fields)
then fetch the record and create PersonName type from the appropriate record fields.
The idea is to define some multi-column custom field PERSON_NAME, which will be expanded by jooq into three "real" fields during the query execution, and packed to the one PersonName object in the result.
Looks like it's possible to do something like this with org.jooq.impl.AbstractField, but I'm wondering, may be there is a solution for such case already.

There are pending feature requests to support this kind of functionality:
https://github.com/jOOQ/jOOQ/issues/2360 (nested records)
https://github.com/jOOQ/jOOQ/issues/2530 (fetch groups)
With out-of-the-box functionality of jOOQ 3.6, you could store those columns somewhere as:
Field<?>[] personName = {
PERSON.SALUTATION,
PERSON.FIRST_NAME,
PERSON.LAST_NAME
};
And then select them as such:
db.select(personName)
.select(... some other fields);

Related

JOOQ arrayAgg array_agg fields as object

I have 2 entities:
record Customer(String name, List<CustomerContact > contactHistory) {}
record CustomerContact(LocalDateTime contactAt, Contact.Type type) {
public enum Type {
TEXT_MESSAGE, EMAIL
}
}
These are persisted in a schema with 2 tables:
CREATE TABLE customer(
"id". BIGSERIAL PRIMARY KEY,
"name" TEXT NOT NULL
);
CREATE TABLE customer_contact(
"customer_id" BIGINT REFERENCES "customer" (ID) NOT NULL,
"type" TEXT NOT NULL,
"contact_at" TIMESTAMPTZ NOT NULL DEFAULT (now() AT TIME ZONE 'utc')
);
I want to retrieve the details of my Customers with a single query, and use the arrayAgg method to add the contactHistory to each customer. I have a query like this:
//pseudo code
DSL.select(field("customer.name"))
.select(arrayAgg(field("customer_contact.contact_at")) //TODO How to aggregate both fields into a CustomerContact object
.from(table("customer"))
.join(table("customer_contact")).on(field("customer_contact.customer_id").eq("customer.id"))
.groupBy(field("customer_contact.customer_id"))
.fetchOptional()
.map(asCustomer());
The problem I have with this is that arrayAgg will only work with a single field. I want to use 2 fields, and bind them into a single object (CustomerContact) then use that as the basis for the arrayAgg
Apologies if I have not explained this clearly! Any help much appreciated.
Rather than using ARRAY_AGG, how about using the much more powerful MULTISET_AGG or MULTISET to get the most out of jOOQ's type safe capabilities? Combine that with ad-hoc conversion for type safe mapping to your Java records, as shown also in this article. Your query would then look like this:
Using MULTISET_AGG
List<Customer> customers =
ctx.select(
CUSTOMER.NAME,
multisetAgg(CUSTOMER_CONTACT.CONTACT_AT, CUSTOMER_CONTACT.TYPE)
.convertFrom(r -> r.map(Records.mapping(CustomerContact::new))))
.from(CUSTOMER)
.join(CUSTOMER_CONTACT).on(CUSTOMER_CONTACT.CUSTOMER_ID.eq(CUSTOMER.ID))
.groupBy(CUSTOMER_CONTACT.CUSTOMER_ID)
.fetch(Records.mapping(Customer::new));
Note that the entire query type checks. If you change anything about the query or about your records, it won't compile anymore, giving you additional type safety. This is assuming that youre Type enum is either:
Generated from a PostgreSQL ENUM type
Converted automatically using an enum converter, attached to generated code
Depending on your tastes, using implicit joins could slightly simplify the query for you?
List<Customer> customers =
ctx.select(
CUSTOMER_CONTACT.customer().NAME,
multisetAgg(CUSTOMER_CONTACT.CONTACT_AT, CUSTOMER_CONTACT.TYPE)
.convertFrom(r -> r.map(Records.mapping(CustomerContact::new))))
.from(CUSTOMER_CONTACT)
.groupBy(CUSTOMER_CONTACT.CUSTOMER_ID)
.fetch(Records.mapping(Customer::new));
It's not a big deal in this query, but in a more complex query, it can reduce complexity.
Using MULTISET
An alterantive is to nest your query instead of aggregating, like this:
List<Customer> customers =
ctx.select(
CUSTOMER.NAME,
multiset(
select(CUSTOMER_CONTACT.CONTACT_AT, CUSTOMER_CONTACT.TYPE)
.from(CUSTOMER_CONTACT)
.where(CUSTOMER_CONTACT.CUSTOMER_ID.eq(CUSTOMER.ID))
).convertFrom(r -> r.map(Records.mapping(CustomerContact::new))))
.from(CUSTOMER)
.fetch(Records.mapping(Customer::new));
Code generation
For this answer, I was assuming you're using the code generator (you should!), as it would greatly contribute to this code being type safe, and make this answer more readable.
Much of the above can be done without code generation (except implicit joins), but I'm sure this answer could nicely demonstrate the benefits it terms of type safety.

Using Spring JPA, can I save the return value for a function in my Entity?

Attempting, using Spring Boot with JPA, to read data from an API, and then save that data into my DB with some alterations.
I have an Entity like:
#Entity
#Table(name="BOOK_TABLE")
public class Book{
//...some normal variables (name, pageCount, etc.)
//I don't want this in the table
#Transient SeriesSummary seriesSummary;
//I want to save the output of this to the table
public Integer getSeriesId() {
//... does some stuff that figures out the id from the SeriesSummary
return seriesId;
}
}
So that I could map a book to the BOOK_TABLE:
book_id, name, pageCount..., seriesId
This seriesId could be used to look up a Series object in a SERIES_TABLE. But the Series object is different than the SeriesSummary object from my Book class. The SeriesSummary is just something coming from the API that provides the URL to look up the Series from the foreign API and its Id. I have no interest in storing this URL. I just want to grab the Book object, and then I will look up the Series object, store both, and map them together in my DB.
I can't seem to get Spring + JPA to save the output of the getSeriesId function. Can you only save class variables? If so how would you work around this?
The format of these objects is structured in the same way as the foreign API so that I can download the data objects using RestTemplate. Do I need to create a separate Class just for saving the columns I want?
Thank you!

Hibernate implementation by pass in different types of params

So I am able to get Company information by passing in Company ID with Hibernate, code is as follows:
public Company getCompanyById(Integer companyId) {
Company company = (Company) getCurrentSession().get(Company.class, companyId);
return company;
}
Now what I am trying to do is to pass in company name, address and cell phone number to get the company. The method in my mind is like this:
public Company getCompanyByNameAddressCellphone(String companyName, String address, Integer cellPhoneNumber);
How do I implement it?
You can use Hibernate Criteria to achieve this. It would look something like:
public Company getCompanyByNameAddressCellphone(String companyName, String address, Integer cellPhoneNumber) {
Criteria criteria = getCurrentSession().createCriteria(Company.class);
criteria.add(Restrictions.eq("companyName", companyName));
criteria.add(Restrictions.eq("address", address));
criteria.add(Restrictions.eq("cellPhoneNumber", cellPhoneNumber));
return criteria.uniqueResult();
}
In this scenario, the String values provided to the Restrictions#eq call are the property names of your Company entity.
If you don't want to match exact String values and prefer using like, you can use Restrictions#like or Restrictions#ilike (case insensitive version).
You can either use Criteria/DetachedCriteria or HQL. A Criteria instance or a Query instance can both be retrieved from the session (referenced by the same getCurrentSession() call in your example).
Not sure what version of Hibernate you're working with, but here is the documentation on querying: https://docs.jboss.org/hibernate/core/3.3/reference/en-US/html/objectstate.html#objectstate-querying
the key points are that your property names (Company.name, Company.address, etc.) are used to query rather than the DB column names, which your code shouldn't be expected to know.
My personal preference is for DetachedCriteria. Used with the Restrictions API, you can accomplish about 85% of your querying needs. DetachedCriteria's execution flow is slightly different and documented here:
https://docs.jboss.org/hibernate/core/3.3/reference/en-US/html/querycriteria.html#querycriteria-detachedqueries

Possible to query by key instead of value in Hazelcast (using Predicates)?

In Hazelcast, is it possible to query an IMap based on attributes of a key instead of the values? All the Hazelcast examples show querying by value. E.g., for a map of employees with keys that are strings:
IMap<String, Employee> employees;
The typical search predicates then search based on employee attributes (name, salary, etc). But my case uses more complex keys, such as:
IMap<DataAttributes, DataValue> myData;
So if DataAttributes has fields such as:
class DataAttributes {
String theDescription;
Date theStartTime;
public String getDescription() { return theDescription; }
// etc....
}
I want to write a predicate that can query by the keys, to return an appropriate DataValue object. This does not work:
Predicate pred = Predicates.equal("description", "myDescription");
myData.keySet(pred); // Throws IllegalArgumentException: "There is no suitable accessor for..."
I could roll-my-own as suggested in this answer, but I'd rather use an out-of-the-box solution if I can.
It doesn't matter if I wind up using the Criteria API, or the Distributed SQL Query API. Any working query would be great. Bonus points for a solution that works on nested attributes (i.e.: DataAttributes theStartTime.getYear()).
It is possible using PredicateBuilder (com.hazelcast.query.PredicateBuilder). The PredicateBuilder paradigm allows you to query based on keys, like so:
EntryObject eo = new PredicateBuilder().getEntryObject();
Predicate fredWithStartTimeThisYear = eo.key().get("Description").equal("Fred")
.and(eo.key().get("theStartTime.Year").equal(2015));
Note that you can refer to class members by accessor method ("getter") or field name, as you can see in the above example code. I found this information in the "Mastering Hazelcast" online book, available at hazelcast.org (but you have to fill out a registration form to gain access to it).

How to retrieve #Reference fields / which is best #Reference or #Embedded and SubQuery in MorphiaQuery

I have a mongodb with two model classes say User and UserInfo. The criteria is in User class I have to retrieve a multiple fields around 10 fields like "firstName","lastName", etc and in UserInfo Model class I like to retrieve only one field say "age".
At this moment I referenced the UserInfo class's object to the User class like stated below in the Structure and its stores in the DB as {"firstName","John"},{"lastName","Nash"},{userInfo: userInfoID} but if I make an Embedded Relation then it would store all the userInfo's fields and I think to retrieve one ("age") field it is Unwanted to Embed all the userInfo's fields which inturn will make the application slow I think.
Which scenario should I use whether #Reference or #Embedded, I think Embedded will slow down my response to DB but in the websites its given as reference annotation only slows down querying time and needs some sort of Lazy Loading an all, my structure is like below:
class User extends Model{
public String firstName;
public String lastName;
public String loginTime;
public String logoutTime;
public String emailId; etc,etc......
Some more 10 fields like this+userInfo reference object
#Reference
public UserInfo userInfo;
}
class UserInfo extends Model{
public String emailId;
public String age;
public String sex;
public String address;
public String bank; etc,etc......
Some more 10 fields like this
}
As I stated above I want only age field from UserInfo and all fields of User, so which Annotation is best and #Reference or #Embedded. It will be more helpful if I get a single query for User class in which I can retrieve all fields of User and only "age" field of UserInfo. In short I need a query like this when I go for #Reference relationship
field("userInfo.age") for userInfo.emailId = (MorphiaQuery q = User.createMorphiaQuery;
q.field("firstName").equal("John"); q.field("lastName").equal("Nash"); q.field("loginTime").greaterthan("sometime"))//the complex part where I need age of particular userInfo but I have only the ID of the userInfo since I am using Reference and that Id too got from a **subQuery**....
Please don't write two queries I need a single query or maybe a query with subquery. To be more clear I can tell in SQL language:
SELECT age FROM UserInfo where emailId = u.emailId
(SELECT * FROM User WHERE firstName='John' AND lastName='Nash' AND
logintime='someTime') AS u;
I need this exact same query without writing two morphia queries which consumes more time by referring two tables.
Mongo does not support query across tables / collections. And such page would satisfy you:
MongoDB and "joins"
As in sql, the join query is also build intermediate result set and make query again:
Understanding how JOIN works when 3 or more tables are involved. [SQL]
When you build your model, you should not consider a lot about what single query but structural modeling:
http://docs.mongodb.org/manual/core/data-modeling/
For your case, if you are using embeded, you can make this in one query and specify the fields you need by using queries like:
db.User.find({"some_field":"some_query"},{"firstName":1,....,"userInfo.age":1})
Check projections here:
http://docs.mongodb.org/manual/reference/method/db.collection.find/
If you are using reference or even soft link like using Morphia Key<> to lazy load the UserInfo, it requires two queries.
If it's not real-time application, you can also try mongo map-reduce to merge collection to handle big data, though the map-reduce is too bad for mongo though.
I'm reasonably sure you can't with just one query.

Categories