ObjectBox - ToMany Conditions - java

I have the following code, which selects everything in AEntity.
Box<AEntity> a = boxStore.boxFor(AEntity.class);
return new ObjectBoxLiveData<AEntity>(a.query().build());
AEntity has a ToMany relationship with BEntity:
#Backlink(to = "aEntity")
private ToMany<BEntity> bEntities;
I would like to select everything in AEntity as shown above while checking a property of BEntity. The ideal code would look something like this:
Box<AEntity> a = boxStore.boxFor(AEntity.class);
return new ObjectBoxLiveData<AEntity>(a.query().notEqual(BEntity_.bproperty, "-1").build());
Basically I am saying: "I want everything from AEntity as long as bproperty isn't "-1".
Of course, this doesn't work but is there a way I could achieve this behavior?

Did you try adding a link query for the related entity BEntity? Something like:
queryBuilderA = a.query();
queryBuilderA.link(AEntity_.relation).notEqual(BEntity_.bproperty, -1);
queryBuilderA.build();
https://docs.objectbox.io/queries#add-query-conditions-for-related-entities-links

Related

Collection Query Issues with QueryDSL JPA and PostgreSQL

I'm currently using QueryDSL 3.7.2 with Spring-Data-JPA. I'm building a predicate, then passing it to a repository that extends QueryDslPredicateExecutor and queries a PostgreSQL 9.3 database.
I had everything working correctly, but have run into two scenarios that are causing me issues. I'm not sure if I'm approaching this correctly, and I can't seem to find any examples that quite match the scenarios.
Scenario 1:
For this, I have an entity with a list of children. I wanted an "AND" on two of the child properties, so I used a JPASubQuery:
final QChild any = QParent.parent.children.any();
final JPASubQuery subQuery = new JPASubQuery();
final QChild qChild = QChild.child;
subQuery.from(qChild)
.where(qChild.code.eq(codeValue)
.and(qChild.date.isNull()));
predicateBuilder.and(any.in(subQuery.list(qChild)));
So basically I want to fetch any Parent objects where the Child has a code of codeValue and a null date. This worked perfectly when the Child had a surrogate key in the database (an ID column). This generated the following query when passed to the repository:
select
count(parent0_.parent_id) as col_0_0_
from parent_tab parent0_
where exists (
select 1
from child_tab child1_
where parent0_.parent_id=child1_.parent_id
and (
child1_.child_id
in (
select child2_.child_id
from child_tab child2_
where child2_.status=? and (child2_.date is null)
)
)
)
The problem arises when we change the surrogate key to a natural key with serveral fields (code, status, parent_id and name). The following query is then generated:
select
count(parent0_.parent_id) as col_0_0_
from parent_tab parent0_
where exists (
select 1
from child_tab child1_
where parent0_.parent_id=child1_.parent_id
and (
(child1_.code, child1_.status, child1_.parent_id, child1_.name)
in (
select (child2_.code, child2_.status, child2_.parent_id, child2_.name)
from child_tab child2_
where child2_.status=? and (child2_.date is null)
)
)
)
This isn't valid, and throws the following exception:
ERROR: subquery has too few columns
From what I can gather, any().in(subQuery.list(qChild)) is the part that's causing the problem. From all the examples I've found, this is what's being done - but it's also with a surrogate key. Is there something that I'm missing here?
Scenario 2:
The second scenario is dealing with a PostgreSQL only feature - the pg_trgm extension. This extension is used for fuzzy searching, and allows us to use two specific commands - "similarity(x, y)" and "x % y". The former will return a real number representing how close a match the parameters are, and the second will return a boolean if the values are above 0.3 (by default). Originally I was using the former, like so:
final NumberExpression<Double> nameExpression = NumberTemplate.create(Double.class, "similarity({0}, {1})", QParent.parent.name ConstantImpl.create(name));
predicateBuilder.or(nameExpression.goe(0.3));
This worked perfectly, but unfortunately "similarity(x, y)" doesn't use trigram indexes, so I wanted to change to the "%" operator, which does. I thought it should be as easy as the following:
final BooleanExpression nameExpression = BooleanTemplate.create("{0} % {1}", QParent.parent.name, ConstantImpl.create(name));
predicateBuilder.or(nameExpression.isTrue());
Unfortunately this doesn't work, and throws the following exception:
java.lang.IllegalArgumentException: Parameter value [true] did not match expected type [java.lang.String (n/a)]
at com.mysema.query.jpa.impl.JPAUtil.setConstants(JPAUtil.java:55) [querydsl-jpa-3.7.2.jar:]
at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:130) [querydsl-jpa-3.7.2.jar:]
at com.mysema.query.jpa.impl.AbstractJPAQuery.count(AbstractJPAQuery.java:81) [querydsl-jpa-3.7.2.jar:]
at org.springframework.data.jpa.repository.support.QueryDslJpaRepository.findAll(QueryDslJpaRepository.java:141) [spring-data-jpa-1.9.2.RELEASE.jar:]
The problem seems to be that it's expecting a String instead of a Boolean for the JavaType in the AbstractJPAQuery. Excluding isTrue() results in the following exception:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: % near line 3, column 19
The query is the following:
select count(parent)
from com.test.Parent parent
where parent.name % ?1
I solved this by using a custom dialect, and registering the following function:
registerFunction("sim", new SQLFunctionTemplate(StandardBasicTypes.BOOLEAN, "?1 % ?2"));
Then I can use the following:
final BooleanExpression nameExpression = BooleanTemplate.create("sim({0}, {1})", QParent.parent.name, ConstantImpl.create(name));
predicateBuilder.or(nameExpression.isTrue());
When expanding on this, I wanted to also check the name of the child entities. This led to the following:
final BooleanExpression childExpression = BooleanTemplate.create("sim({0}, {1})", QParent.parent.children.any().name, ConstantImpl.create(name));
predicateBuilder.or(childExpression.isTrue());
This, however, doesn't work, and throws the following exception:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: exists near line 3, column 7
The query is:
select count(parent)
from com.test.Parent parent
where exists (select 1
from parent.children as parent_children_4652c
where sim(parent_children_4652c.name, ?1)) = ?2
The exception seems to point to the "exists", but I'm not sure why. As a result, I tried to create a sub-query (like scenario 1):
final BooleanExpression childExpression = BooleanTemplate.create("sim({0}, {1})", QChild.child.name, ConstantImpl.create(name));
final QChild child = QChild.child;
final JPASubQuery subQuery = new JPASubQuery();
subQuery.from(child)
.where(childExpression.isTrue());
predicateBuilder.or(QParent.parent.children.any().in(subQuery.list(child)));
This, however, runs into the same problem as scenario 1, where the child entity has a composite key, and the any().in() doesn't seem correct.
So there's a few questions here:
Is there a way to use the % operator without registering a custom function in the dialect?
Am I querying the children correctly with the sim() function?
What's the correct way to craft a sub-query with a composite key?
Any additional pointers or help would be greatly appreciated too.
Figured out how to do both (though I haven't answered all questions from Scenario 2). My problem was thinking that I wanted the entity returned. A good night's sleep made me see it clearly.
Instead of returning qChild from the subquery list, I should have been returning its parent ID. Then I simply needed to check if the parent ID was in that list:
final String code = "code";
final String name = "name";
final JPASubQuery subQuery = new JPASubQuery();
final QParent parent = QParent.parent;
final QChild child = QChild.child;
subQuery.from(child)
.where(child.id.code.eq(code)
.and(child.id.name.eq(name)));
predicateBuilder.or(parent.id.in(subQuery.list(child.id.parentId)));
For the second scenario, I kept the custom Dialect with the registerFunction for my trigram operator, and used the following subquery:
final String name = "name";
final QParent parent = QParent.parent;
final QChild child = QChild.child;
final BooleanExpression newExpression = BooleanTemplate.create("sim({0}, {1})",
child.id.name, ConstantImpl.create(name));
final JPASubQuery subQuery = new JPASubQuery();
subQuery.from(child)
.where(newExpression.isTrue());
predicateBuilder.or(parent.id.in(subQuery.list(child.id.parentId)));
Everything is working correctly, though I still wonder if there's a way to simply use the trigram operator without registering a custom function.

Constructing DTO:s with Querydsl and ConstructorExpression.create()

I have a problem involving querydsl and DTO:s
I have some query object:
QPerson person = QPerson.person;
QExamCode examCode = QExamCode.examCode;
QExamGrade examGrade = QExamGrade.examGrade;
QProgram gradeProgram = examGrade.program;
From them I try to query and list instances of a DTO class (that is not an entity) that is called CompletedCreditsSummary.
CompletedCreditsSummary has a constructor which takes: Long,Long,Float.
JPQLQuery query = new JPAQuery(manager);
query = query.from(person, examCode, examGrade);
query = query.where(person.studies.examGrades.contains(examGrade).and(examGrade.examCode.eq(examCode)).and(examGrade.passed.isTrue()));
I am able to do this (Without group by and with CompletedCreditsSummary requiering all the parameters it needs to be able to create person and program objects, in this case simplified to person.id and program.id)
ConstructorExpression.create(CompletedCreditsSummary.class,person.id,program.id,examCode.credits);
return query.list(completedCreditsSummaryExpression);
This works. But when I want to add this to the query:
query.groupBy(person, examGrade.program);
and create CompletedCreditssummary with examCode.credits.sum() i.e.
ConstructorExpression.create(CompletedCreditsSummary.class,person.id,gradeProgram.id,examCode.credits.sum());
instead of
ConstructorExpression.create(CompletedCreditsSummary.class,person.id,gradeProgram.id,examCode.credits);
I get a: java.lang.IllegalArgumentException: argument type mismatch.
The question here is what the difference between examCode.credits (NumberPath) and examcode.credits.sum() (NumberExpression) and what I could do to solve my problem.
As I am learning querydsl by trial and error there is probably something fundamental that I have overlooked. Would really appreciate any help!
Regards Rasmus
EDIT: Preferably I would something like this to work (with group by and CompletedCreditsSummary constructor taking Person,Program,Float.):
ConstructorExpression<Person> personExpression = ConstructorExpression.create(Person.class,person.id);
ConstructorExpression<Program> programExpression = ConstructorExpression.create(Program.class,gradeProgram.id);
ConstructorExpression<CompletedCreditsSummary> completedCreditsSummaryExpression = ConstructorExpression.create(CompletedCreditsSummary.class,personExpression,programExpression,examCode.credits.sum());
EDIT: Got it to work by having the CompletedCreditsSummary Constructor accepting: Long,Long,Number. That is I changed Float to Number. This is not an ideal solution but at least it works.
Try something like this
JPAQuery query = new JPAQuery(manager);
query.from(person, examCode, examGrade)
.where(
person.studies.examGrades.contains(examGrade),
examGrade.examCode.eq(examCode),
examGrade.passed.isTrue())
.groupBy(person, examGrade.program)
.list(ConstructorExpression.create(
CompletedCreditsSummary.class,
person, examGrade.program, examCode.credits.sum()));
You need to make sure that the argument for ConstructorExpression after the class are compatible with the arguments to the constructor you want to invoke. Replacing entities with ids caused your problems.

can hibernate hsql automatically map the query result to a class?

I wrote a hsql:
String queryString = "select t1.a, t1.b, t2.c from table1 t1, table2 t2 where t1.id = t2.id";
and then I have a class:
class test{
String a;
String b;
String c
....//other getter and setter
}
I tried:
List = getHibernateTemplate().find(queryString);
this doesn't work, when I use test object in jsp page, it will throw out exception.
I have to manually create a test object:
List<Object[]> list = getHibernateTemplate().find(queryString);
test.seta(list.get(0)[0]);
is it possible for hibernate to automatically map the class for me in hsql ?
If you have a mapping for both table1 and table2 (see Prashant question above) you can do something like:
String queryString = "select t1 from table1 t1
inner join t1.table2 t2";
After you run the query you should have a list of t1 objects.
for(Table1 t1:listOfTable1Objects) {
t1.getA(); //for example or whatever you want to do with your object.
}
The Problem is that you do not write a HQL query. You just write a normal SQL query. In HQL, because the hibernate make the mapping from table to class, you cannot make a projection. So, if you write something like
String query = "FROM Class1 WHERE ome_condition;
without the SELECT clause, the Hibernate will be able to convert the result in the proper object.
You can see more about this here: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/queryhql.html
If you dont have a mapping, you may create a auxiliary class for this. Say ResultClass. Then you add #NamedNativeQuery and #SqlResultSetMapping annotations to the class:
#NamedNativeQuery(name="queryHehehe", query="select t1.field1 f1, t2.field2 f2 from table1 t1, table2 t2", resultSetMapping="mappingHehehe")
#SqlResultSetMapping(name="mappingHehehe", entities={
#EntityResult(entityClass=my.clazz.AuxiliaryClass.class, fields = {
#FieldResult(name="id", column="f1"),
#FieldResult(name="other_property", column="f2")
}),
})
public class AuxiliaryClass {
public Long id;
public String other_property;
}
I have never used this, but can work. Good luck.
If you need a query to return values from multiple tables and create an object of an unmapped class, then you need to either do what you're doing here, or use a ResultTransformer.
In order to do this with HibernateTemplate, you'll need to change the way you use the template, possibly using execute(HibernateCallback action), as you'll need to convert the sql query to a Criteria as described in Hibernate Reference Native SQL Chapter.
If you do want to try this, you'll probably want to use an AliasToBeanResultTransformer or AliasToBeanConstructorResultTransformer rather than writing your own transformer.

How can I get Toplink generated query by using getTranslatedSQLString?

So what I'm doing is creating a subquery that gets a list of ID values, then the main query gets all the necessary values and adds ordering.
What I have is this:
ReportQuery querySub = new ReportQuery(Predmet.class, generatedExpression);
querySub.addAttribute("m_id");
DatabaseRow row = new DatabaseRow();
querySub.prepareCall(getSession(), row);
// This part is the problem
String sql = querySub.getTranslatedSQLString(getSession(), row);
The problem with this code is that it doesn't return TranslatedSQLString, it returns the same result as querySub.getSQLString(). Now in all the example code I saw, they either instanced row as a new object or didn't bother to write from where they got the reference but whatever the case, this doesn't work (TopLink version issue?). I'm guessing I need to populate the DatabaseRow object myself, but I can't find any example online.
I didn't manage to find any way to do this by using getTranslatedSQLString. I suppose the DatabaseRow needs to be populated, but I have yet to find the proper way. For now, I'm using "bruteforce" substitution, I "memorize" all of my parameters and do a find/replace on each "?" sign in the query.
you need get session like this:
JpaHelper.getDatabaseSession(getEntityManager().getEntityManagerFactory());
The Database that you need is:
TypedQuery<T> typedQuery = getEntityManager().createQuery(cq);
DatabaseQuery databaseQuery = typedQuery.unwrap(JpaQuery.class).getDatabaseQuery();
So the final example is:
Session session = JpaHelper.getDatabaseSession(getEntityManager().getEntityManagerFactory());
DatabaseQuery databaseQuery = null;
if(typedQuery instanceof EJBQueryImpl)
databaseQuery = ((EJBQueryImpl<T>)typedQuery).getDatabaseQuery();
else
databaseQuery = typedQuery.unwrap(JpaQuery.class).getDatabaseQuery();
sql = databaseQuery.getTranslatedSQLString(session, databaseQuery.getTranslationRow());
That work for me.

Google App Engine / Java - inconsistent ORDER BY

I am using JPA on top of App Engine (I am quite new to both) and I am currently facing a behaviour I do not understand.
Each time I refresh the page, the order of the fetched items changes.
Here is the code snippet:
Set<Cast> results = new HashSet<Cast>();
EntityManager entityManager = entityManagerFactory.createEntityManager();
Query query = entityManager.createQuery(FIND_ALL_SORTED_BY_DESCENDING_BROADCAST_DATE);
#SuppressWarnings("unchecked")
List<Cast> casts = query.getResultList();
for (Cast cast : casts) {
if (verifySecondaryFields(cast)) {
results.add(synchronizeTechnicalFields(cast));
}
}
entityManager.close();
return Collections.unmodifiableSet(results);
where FIND_ALL_SORTED_BY_DESCENDING_BROADCAST_DATE actually is SELECT cast FROM Cast cast ORDER BY cast.broadcastDate DESC.
entityManagerFactory is an autowired member of my repository class.
The thing is the ORDER BY clause seems to be ignored and the results show up randomly. Can you spot what is wrong?
Sets don't preserve order. Lists do. Try List<Cast> = new ArrayList<Cast>(); and carry on from there.

Categories