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

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).

Related

How can we query in hibernate which returns a Map object?

I have an entity Subscription which has the following properties
name,BillOfLading,validTo (Date),ValidFrom (Date)
Sample Data:
Name BillOfLading ValidTo ValidFrom
A 100101 1/28/2016 3/28/2017
A 100102 1/29/2016 3/29/2017
B 100103 1/30/2016 3/30/2017
C 100104 1/31/2016 3/31/2017
A 100105 1/28/2016 3/28/2017
Here I'm trying to get result in the form Map
Map<String,List<Subscription>>
The map contains a (key,value) pair,where Name as key and List of subscriptions that belongs to specified name.
Suppose the Name A has 3 records So now the key will be A and value will be list of Subscriptions that belongs to A.
And also I need to sort Map based on the count of A subscription there in DB.
Note: The above is a shortened story of my scenario. So, I'm not putting my actual code.
Please help me if there is any possibility of returning Map in hibernate.
I dont see being possible to return Map<String,List<Subscription>> by a JPQL/HQL query.
I had return Maps from JPQL for example like this:
#Query("SELECT new map(r.code, r.name) FROM Substance r GROUP BY r.code, r.name")
List<Map<String, String>> select();
If you need a that complex return type have a look at Hibernate's ResultTransformer interface and it's implementations, which can customize the result you want. I had never used this. There is a blog post which show the usage of this interface: Why you should use the Hibernate ResultTransformer to customize result set mappings

Hibernate: Fetching columns with their aliases

Consider this trivial query:
SELECT 1 as first, 2 as second
When using Hibernate we can then do something like:
em.createNativeQuery(query).fetchResultList()
However, there seem to be no way of getting the aliases (or column names). This would be very helpful for creating List<Map<String, Object>> where each map would be a row with their aliases, for instance in this case: [{first: 1, second: 2}].
Is there a way to do something like that?
I would suggest a bit different approach which may meet your needs.
In JPA 2.1 there is a feature called "result set mapping".
Basically you have to define a POJO class which would hold the result values (all the values must be passed using the constructor):
public class ResultClass{
private String fieldOne;
private String fieldTwo;
public ResultClass(String fieldOne, String fieldTwo){
this.fieldOne = fieldOne;
this.fieldTwo = fieldTwo;
}
}
Then you have to declare the mapping on one of your entities (does not matter on which, it just has to be a declated #Entity):
#SqlResultSetMapping(name="ResultMapping", classes = {
#ConstructorResult(targetClass = ResultClass.class,
columns = {#ColumnResult(name="columnOne"), #ColumnResult(name="columnTwo")})
})
The columnOne and columnTwo are aliases as declared in the select clause of the native query.
And finally use in the query creation:
List<ResultClass> results = em.createNativeQuery(query, "ResultMapping").getResultList();
In my opinion this is more elegant and "a level above" solution as you are not working with a generic Map key/values pairs but with a concrete POJO class.
You can use ResultTransformer interface . Implement custom mapper for mapping values with aliases.
here is example https://vladmihalcea.com/why-you-should-use-the-hibernate-resulttransformer-to-customize-result-set-mappings/
with ResultTransformer you can easy customize result set type , especially if you need aliases

jooq single query with one to many relationship

I have a table experiment and a table tags. There may be many tags for one experiment.
schema:
-------- --------
|Table1| 1 n |Table2|
| | <--------------> | |
| | | |
-------- --------
(experiment) (tags)
Is it possible to create a query with jooq which returns the experiments and the corresponding List of tags?
something like Result<Record> where Record is a experimentRecord and a list of Tags, or a map<experimentRecord, List<TagRecord>.
I also have a query which returns only one result, is there something convenient out there?
EDIT: java8, newest jooq.
There are many ways to materialise a nested collection with SQL, and / or with jOOQ. I'm just going through some of them:
Using joins
If you don't deeply nest those collections, denormalising (flattening) your results with a JOIN might do the trick for you, without adding too much overhead as data is being duplicated. Essentially, you'll write:
Map<ExperimentRecord, Result<Record>> map =
DSL.using(configuration)
.select()
.from(EXPERIMENT)
.join(TAGS)
.on(...)
.fetchGroups(EXPERIMENT);
The above map contains experiment records as keys, and nested collections containing all the tags as values.
Creating two queries
If you want to materialise a complex object graph, using joins might no longer be optimal. Instead, you probably want to collect the data in your client from two distinct queries:
Result<ExperimentRecord> experiments =
DSL.using(configuration)
.selectFrom(EXPERIMENT)
.fetch();
And
Result<TagsRecord> tags =
DSL.using(configuration)
.selectFrom(TAGS)
.where(... restrict to the previous experiments ...)
.fetch();
And now, merge the two results in your client's memory, e.g.
experiments.stream()
.map(e -> new ExperimentWithTags(
e,
tags.stream()
.filter(t -> e.getId().equals(t.getExperimentId()))
.collect(Collectors.toList())
));
Nesting collections using SQL/XML or SQL/JSON
This question didn't require it, but others may find this question in search for a way of nesting to-many relationships with jOOQ. I've provided an answer here. Starting with jOOQ 3.14, you can use your RDBMS's SQL/XML or SQL/JSON capabilities, and then use Jackson, Gson, or JAXB to nest collections like this:
List<Experiment> experiments =
ctx.select(
EXPERIMENT.asterisk(),
field(
select(jsonArrayAgg(jsonObject(TAGS.fields())))
.from(TAGS)
.where(TAGS.EXPERIMENT_ID.eq(EXPERIMENT.ID))
).as("tags")
)
.from(EXPERIMENT)
.fetchInto(Experiment.class);
Where Experiment is a custom Java class like this:
class Experiment {
long id;
String name;
List<Tag> tags;
}
class Tag {
long id;
String name;
}
Nesting collections using MULTISET
Even better than the above, you can hide using SQL/XML or SQL/JSON behind jOOQ 3.15's new MULTISET operator support. Assuming the above Java classes are Java 16 records (or any other immutable classes), you can even map nested collections type safely into your DTOs:
List<Experiment> experiments =
ctx.select(
EXPERIMENT.ID,
EXPERIMENT.NAME,
multiset(
select(TAGS.ID, TAGS.NAME)
.from(TAGS)
.where(TAGS.EXPERIMENT_ID.eq(EXPERIMENT.ID))
).as("tags").convertFrom(r -> r.map(Records.mapping(Tag::new)))
)
.from(EXPERIMENT)
.fetch(Records.mapping(Experiment::new));
Where Experiment is a custom Java class like this:
record Experiment(long id, String name, List<Tag> tags) {}
record Tag(long id, String name) {}
See also this blog post for more information.
You can now use SimpleFlatMapper to map your result to a Tuple2<ExperimentRecord, List<TagRecord>>. All you need to do is.
1 - create a mapper, specify the key column, assumed it would be id
JdbcMapper mapper =
JdbcMapperFactory
.newInstance()
.addKeys(EXPERIMENT.ID.getName())
.newMapper(new TypeReference<Tuple2<ExperimentRecord, List<TagRecord>>>() {});
2 - use the mapper on the ResultSet of your query
try (ResultSet rs = DSL.using(configuration)
.select()
.from(EXPERIMENT)
.join(TAGS)
.on(...)
.fetchResultSet()) {
Stream<Tuple2<ExperimentRecord, List<TagRecord>>> stream = mapper.stream(rs);
....
}
See here for more details

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

JOOQ multi-field custom type converter

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);

Categories