my2iu / jinq Goto Github PK
View Code? Open in Web Editor NEWLINQ-style queries for Java 8
License: Other
LINQ-style queries for Java 8
License: Other
Lets say I have 3 EntitiesE1, E2
and E3
now If I create a stream that joinsE1
, E2
and then left outer join E3
but only select a pair of E1
, E2
, the translated query still joins E3.
So basically IMO it should be something like that
E3
is (selected/used by a selected element) then perform the joinE3
was not selected but joined with other entities then
E3
is neither selected nor joined, do not perform the joinIs there a way to avoid that ?
The general idea is, to try to create a query that is a bit general, which I might us as is, or use a more specific version of it by selecting tuned entities on API levels, other than creating several custom queries for each API call
Example Code:
The entities join may or may not make sense but the idea is there.
@Test
public void Test(){
JinqStream<Tuple3<Customer,Sale,Lineorder>> customers = streams.streamAll(em, Customer.class)
.join((val, source) -> source.stream(Sale.class))
.where(pair -> pair.getOne().getSales().contains(pair.getTwo()))
.select(pair -> new Pair<Customer,Sale>(pair.getOne(),pair.getTwo()))
.leftOuterJoin(t -> JinqStream.from(t.getTwo().getLineorders()))
.select(tuple3 -> new Tuple3<Customer, Sale, Lineorder>(tuple3.getOne().getOne(),tuple3.getOne().getTwo(),tuple3.getTwo()))
;
// Now if I do a modification on this stream, and ask only for Customers and Sales
JinqStream<Pair<Customer,Sale>> customersWithoutLineOrders =
customers.select(pair -> new Pair<Customer, Sale>(pair.getOne(),pair.getTwo()));
String query = customersWithoutLineOrders.getDebugQueryString();
// Now the resulting query should not include the join for Lineorders because its neither selected nor used in any further joins
assertNotEquals("SELECT A, B FROM Customer A, Sale B LEFT OUTER JOIN B.lineorders C WHERE B IN (SELECT D FROM A.sales D)", query);
// The resulting query should only include Customers and Sales join
assertEquals("SELECT A, B FROM Customer A, Sale B WHERE B IN (SELECT C FROM A.sales C)", query);
}
Another Example is a useless join
JinqStream<Pair<Customer,Sale>> customers = streams.streamAll(em, Customer.class)
.join((val, source) -> source.stream(Sale.class))
.where(pair -> pair.getOne().getSales().contains(pair.getTwo()))
.select(pair -> new Pair<Customer,Sale>(pair.getOne(),pair.getTwo()))
.leftOuterJoin(t -> JinqStream.from(t.getTwo().getLineorders()))
.select(pair -> new Pair<Customer,Sale>(pair.getOne().getOne(), pair.getOne().getTwo()))
;
Using boolean methods in entities starting with is, like isRead, throws an exception org.jinq.rebased.org.objectweb.asm.tree.analysis.AnalyzerException: Unknown method
using getRead does not..
.where(pair -> !pair.getTwo().isRead() )
throws exception
I have spend 2 days trying to figure it out but without success so i wonder if it is a bug after all..
I am trying to group some data.
If my query is
streams
.streamAll(em, ParastatikaEntity.class)
.where(c -> c.getDate_created().after(from))
.where(c -> c.getDate_created().before(to))
.where(c -> c.getEidos().getTameio_affect() == 1)
.group(c -> c.getSynallassomenos().getName(),(name,stream)-> stream.sumBigDecimal(c -> c.getTotal_no_vat()))
.forEach(pair -> System.out.println(pair.getOne() + " " + pair.getTwo()));
it works fine, if i try to put it on a List with this
List<Pair<String,BigDecimal>> result =
streams
.streamAll(em, ParastatikaEntity.class)
.where(c -> c.getDate_created().after(from))
.where(c -> c.getDate_created().before(to))
.where(c -> c.getEidos().getTameio_affect() == 1)
.group(c -> c.getSynallassomenos().getName(),(name,stream)-> stream.sumBigDecimal(c -> c.getTotal_no_vat()));
i get
incompatible types: no instance(s) of type variable(s) U,V exist so that JPAJinqStream<Pair<U,V>> conforms to List<Pair<String,BigDecimal>>
where U,V,T are type-variables:
U extends Object declared in method <U,V>group(Select<T,U>,AggregateGroup<U,T,V>)
V extends Object declared in method <U,V>group(Select<T,U>,AggregateGroup<U,T,V>)
T extends Object declared in interface JPAJinqStream
Do i miss something here?
It is also not labled as Java only in the documentation.
What needs to be done to support sub-queries like below so we don't get duplicate person records? JPQL seems to support this query format. Doing a JINQ's join and distinct seems to throw up errors when combined with paging and sorting.
SELECT * FROM Person
WHERE ID IN (SELECT ToId FROM Friends WHERE FromId = :personId )
I investigated the problem @SalehAly reported here a bit.
I have SuperClass
and an extending SubClass
. SuperClass
has a public getter function, which I manually register using registerAssociationAttribute
. If I stream SubClass.class
and try to use that getter function, I'll get AnalyzerException: Unknown method foo encountered
. Even if I register the method again on SubClass
(which doesn't fail because the method is inherited), I still get the same error.
I am looking a way to search string functions (compare , startsWith ) with case insensitive. Is there a a way to do that?
stream.sortedBy(i -> i.getWeight()).count() : Could not translate code to a query
stream.count() : works
I tried the following
List<Pair<String, BigDecimal>> result2
= streams
.streamAll(em, ParastatikaEntity.class)
.where(c -> c.getDate_created().after(from))
.where(c -> c.getDate_created().before(to))
.where(c -> c.getEidos().getTameio_affect() == 2)//pistotika
.where(c -> c.getSynallassomenos().getSynaltype() == 1)//gia pelates
.group(c -> c.getSynallassomenos().getName(), (name, stream) -> stream.sumBigDecimal(c -> c.getTotal_no_vat()).negate())
.toList();
Getting the following error with negate()
Caused by: org.jinq.rebased.org.objectweb.asm.tree.analysis.AnalyzerException: Unknown method java/math/BigDecimal:negate()Ljava/math/BigDecimal; encountered
at ch.epfl.labos.iu.orm.queryll2.symbolic.BasicSymbolicInterpreter.naryOperation(BasicSymbolicInterpreter.java:367)
at org.jinq.rebased.org.objectweb.asm.tree.analysis.Frame.execute(Unknown Source)
at ch.epfl.labos.iu.orm.queryll2.path.CodePath.calculateReturnValueAndConditions(CodePath.java:148)
at ch.epfl.labos.iu.orm.queryll2.path.TransformationClassAnalyzer.analyzeMethod(TransformationClassAnalyzer.java:440)
Running this code, based on the sample:
lineorders.where(_.getSale.getCustomer.getDebt > 0)
.group(_.getSale.getCustomer, (c: Customer, s)=> s.sumBigDecimal(_.getTotal) )
.sortedBy(_._2)
similar to the Java code (which works):
lineorders().where(l->l.getSale().getCustomer().getDebt() > 0)
.group(l2 -> l2.getSale().getCustomer(), (customer, stream) -> stream.sumBigDecimal(l -> l.getTotal()))
.sortedBy(p -> p.getTwo())
gives the following exception:
Exception in thread "main" java.lang.IllegalArgumentException: Could not analyze lambda code
at org.jinq.jpa.transform.LambdaAnalysis.fullyAnalyzeClassAsLambda(LambdaAnalysis.java:123)
at org.jinq.jpa.transform.ScalaLambdaInfo.fullyAnalyze(ScalaLambdaInfo.java:25)
at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambdas(JPAQueryComposer.java:320)
at org.jinq.jpa.JinqJPAScalaIterator.groupToTuple(JinqJPAScalaIterator.scala:221)
at org.jinq.jpa.JinqJPAScalaIterator.group(JinqJPAScalaIterator.scala:226)
at SampleMain.runSampleQueries(SampleMain.scala:74)
at SampleMain$.main(SampleMain.scala:26)
at SampleMain.main(SampleMain.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: org.objectweb.asm.tree.analysis.AnalyzerException: Unknown method org/jinq/orm/stream/scala/JinqIterator:sumBigDecimal(Lscala/Function1;)Ljava/math/BigDecimal; encountered
at ch.epfl.labos.iu.orm.queryll2.symbolic.BasicSymbolicInterpreter.naryOperation(BasicSymbolicInterpreter.java:367)
at org.objectweb.asm.tree.analysis.Frame.execute(Unknown Source)
at ch.epfl.labos.iu.orm.queryll2.path.CodePath.calculateReturnValueAndConditions(CodePath.java:148)
at ch.epfl.labos.iu.orm.queryll2.path.TransformationClassAnalyzer.analyzeMethod(TransformationClassAnalyzer.java:365)
at ch.epfl.labos.iu.orm.queryll2.path.TransformationClassAnalyzer.analyzeLambdaMethod(TransformationClassAnalyzer.java:331)
at org.jinq.jpa.transform.LambdaAnalysis.analyzeLambdaClass(LambdaAnalysis.java:238)
at org.jinq.jpa.transform.LambdaAnalysis.fullyAnalyzeClassAsLambda(LambdaAnalysis.java:113)
... 12 more
code
...streamAll(Content.class)
.where(content->content.getEntityType().equals(EntityType.Book)
&& content.getEntityId() == entityId
&& content.getType().equals(NodeType.Item)).sortedBy(content->content.getParentId()).sortedBy(content->content.getWeight())
.select((content)->new ContentModel(
content.getId(),
content.getParentId(),
content.getType(),
content.getTitle(),
content.getContent(),
content.getEntityId(),
content.getEntityType(),
content.getWeight(),
content.getIndex()
)
);
is translated to:
SELECT A FROM Content A WHERE A.entityType = com.jingfangpai.common.statics.EntityType.Book AND A.entityId = :param0 AND A.type = com.jingfangpai.common.statics.NodeType.Item ORDER BY A.weight ASC, A.parentId ASC
the sorting field are not in order.
It is pre-requisite for any modern project. The best would be to just restructure it for Maven. There is also EntityGenerator that will need a Maven plugin so the consumers can integrate it into their projects easily.
I can help with all that but the transition will require moving a lot of files and could not be done in parallel with a significant amount of other work... But anyway it is best for the project to do it early.
Also there is a question about the package names, the project now is under jinq.org umbrella but there are a lot of stuff in ch.* packages. It there is no good reason to keep them there it would be good to also change packaging so it is associated with the project, i.e. org.jing.*
From the sample:
// Hibernate seems to generate incorrect metamodel data for some types of
// associations, so we have to manually supply the correct information here.
streams.registerAssociationAttribute(Lineorder.class.getMethod("getSale"), "sale", false);
Why is this, and is it solvable? With this requirement for all relations it seems, it is not practical to use Jinq with JPA. Maybe it is possible to get it from the @OneToMany etc. annotations?
Bien Ly Ngoc noted that:
stream.where(n -> n.getAccount().getId() == accoutId || n.getAccount() == null) ...
is being translated as the HQL
WHERE A.account.id = :param0 OR A.account.id <> :param1 AND A.account IS NULL
which gives the resulting SQL:
WHERE ACCOUNT_ID = 1 OR ACCOUNT_ID <> 1 AND ACCOUNT_ID IS NULL;
Which is not correct since when ACCOUNT_ID is NULL, ACCOUNT_ID <> 1 evaluates to UNKNOWN (ie FALSE)
Hi Ming,
First of all, Jinq seems a very promissing library in comparision to existing options like QueryDSL.
Thanks for that ;)
As i can see in the documentation, when i want to execute a JOIN and FETCH all the related records, i have to do:
List<Country> countries =
streams.streamAll(em, Country.class)
.where(c -> c.getContinent.equals("Europe"))
.joinFetchList(c -> c.getCities())
.toList();
for (Country c: countries) {
System.out.println("Cities in country: " + c.getName());
for (City city: c.getCities()) {
System.out.println(" " + city.getName());
}
}
The problem is that using maven version 1.8.4 of Jinq, the API differs and the main entity is not filled with its related childs, executing one query per each record. See my example:
List<Pair<Master, Detail>> records = reportsStream
.where(m -> m.getId() <= QUERY_LIMIT)
.joinList(m -> m.getDetails())
.toList();
for (Pair<Master, Detail> record : records)
System.out.println(record.getOne().getDetails().size());
See also that current version doesn't offer the joinFetchList method and the return type can't be List as i would expect ...
Any clues on this?
Best regards,
Ricardo
With the following code:
@Entity
public class Product {
private String sku;
...
public Product findSkuDuplicate(ProductRepository repository) {
Product existing = repository.all()
.where(product -> product.sku.equals(sku))
.getOnlyValue();
return existing != null && existing != this ? existing : null;
}
...
}
I get the following error:
[java.lang.IllegalArgumentException: Could not extract code from lambda. This error sometim
es occurs because your lambda references objects that aren't Serializable.] with root cause
java.io.NotSerializableException: org.teavm.flavour.example.model.Product
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.user00.thunk.SerializedLambda.extractLambda(SerializedLambda.java:52)
at org.jinq.jpa.transform.LambdaInfo.analyze(LambdaInfo.java:30)
at org.jinq.jpa.transform.LambdaAnalysisFactory.extractSurfaceInfo(LambdaAnalysisFactory.java:7)
at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambda(JPAQueryComposer.java:269)
at org.jinq.jpa.JPAQueryComposer.where(JPAQueryComposer.java:364)
at org.jinq.jpa.JPAQueryComposer.where(JPAQueryComposer.java:58)
at org.jinq.orm.stream.QueryJinqStream.where(QueryJinqStream.java:45)
at org.jinq.jpa.QueryJPAJinqStream.where(QueryJPAJinqStream.java:86)
at org.jinq.jpa.QueryJPAJinqStream.where(QueryJPAJinqStream.java:12)
at org.teavm.flavour.example.model.Product.findSkuDuplicate(Product.java:80)
I can alter this code to refer to temporary variable that holds this.sku
, but I am still interested why do you need to serialize lambdas?
I tried changing this line in the sample from
.sortedDescendingBy(c -> c.getSalary()
to
.sortedDescendingBy(Customer::getSalary)
and then it fails:
Exception in thread "main" java.lang.IllegalArgumentException: Could not translate code to a query
at org.jinq.jpa.JPAQueryComposer.translationFail(JPAQueryComposer.java:113)
at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambda(JPAQueryComposer.java:285)
at org.jinq.jpa.JPAQueryComposer.sortedBy(JPAQueryComposer.java:377)
at org.jinq.jpa.JPAQueryComposer.sortedBy(JPAQueryComposer.java:58)
at org.jinq.orm.stream.QueryJinqStream.sortedDescendingBy(QueryJinqStream.java:247)
at org.jinq.jpa.QueryJPAJinqStream.sortedDescendingBy(QueryJPAJinqStream.java:215)
at org.jinq.jpa.QueryJPAJinqStream.sortedDescendingBy(QueryJPAJinqStream.java:12)
at SampleMain.runSampleQueries(SampleMain.java:168)
at SampleMain.main(SampleMain.java:43)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: org.jinq.jpa.transform.QueryTransformException: ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitorException: Unhandled symbolic execution operation: (this.salary)
at org.jinq.jpa.transform.SortingTransform.apply(SortingTransform.java:41)
at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambda(JPAQueryComposer.java:281)
... 12 more
Caused by: ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitorException: Unhandled symbolic execution operation: (this.salary)
at org.jinq.jpa.transform.SymbExToColumns.defaultValue(SymbExToColumns.java:52)
at org.jinq.jpa.transform.SymbExToColumns.defaultValue(SymbExToColumns.java:39)
at ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitor.unaryOpValue(TypedValueVisitor.java:82)
at ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValueVisitor.getFieldValue(TypedValueVisitor.java:98)
at ch.epfl.labos.iu.orm.queryll2.symbolic.TypedValue$GetFieldValue.visit(TypedValue.java:206)
at org.jinq.jpa.transform.JPQLQueryTransform.simplifyAndTranslatePathToColumns(JPQLQueryTransform.java:110)
at org.jinq.jpa.transform.JPQLQueryTransform.simplifyAndTranslateMainPathToColumns(JPQLQueryTransform.java:102)
at org.jinq.jpa.transform.JPQLQueryTransform.makeSelectExpression(JPQLQueryTransform.java:36)
at org.jinq.jpa.transform.SortingTransform.apply(SortingTransform.java:28)
... 13 more
I have two queries, which are both working and translatable into sql. The elements streamed in these queries are different and not connected. If I have two pre-filtered JPAJinqStreams and want to join them on one property, Jinq tells me, it is not possible, even though in SQL it is one of the easiest things to do.
Enough on these abstract stuff, let's get to an example:
I have two classes Class1
and Class2
, both of which can be tagged with class Tag
. I don't want List<Tag>
to be a delegate of all classes being able to be tagged (in this example Class1
and Class2
).
To get all valid Tag-Class1-Pairs, this query works perfectly as expected:
streams.streamAll(em, Class1.class)
.join((c, source) -> source.stream(Tag.class))
.where(p -> p.getOne().getId() == p.getTwo().getTaggedId())
.forEach(System.out::println);
Producing:
SELECT A, B FROM Class1 A, Tag B WHERE A.id = B.taggedId
Now I'd like to pre-filter both streams (e.g. permission-wise) and get them separately by calling Service-Functions:
JPAJinqStream<Class1> classStream = service.getFilteredClassStream();
JPAJinqStream<Tag> tagStream = service.getFilteredTagStream();
classStream.join(c -> tagStream)
.where(p -> p.getOne().getId() == p.getTwo().getTaggedId())
.forEach(System.out::println);
This for whatever reason results in java.lang.reflect.InvocationTargetException Caused by: java.lang.IllegalArgumentException: Could not extract code from lambda. This error sometimes occurs because your lambda references objects that aren't Serializable.
But actually there is no major difference semantic-wise. In the first example, I create the new stream from inside the stream. In the second example I use one from outside. In the first example source.stream
produces JinqStream<U>
, which is the same as getFilteredTagStream()
returning JPAJinqStream<Tag> extends JinqStream<Tag>
.
So my question is: How do I join two different streams, produced in different contexts?
Hi again Ming,
I'm trying to query a table with a "LIKE" expressión removing diacritical marks. Something like:
private String clean(String name) {
String result = Normalizer.normalize(name, Normalizer.Form.NFD);
return result.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
}
...
JinqJPAStreamProvider streams = new JinqJPAStreamProvider(entityManager.getMetamodel());
JPAJinqStream<Master> reportsStream = streams.streamAll(entityManager, Master.class);
List<Master> records = reportsStream
.where(master -> clean(master.getName()).indexOf("values") > 0)
.toList();
But i get an error:
java.lang.IllegalArgumentException: Could not extract code from lambda. This error sometimes occurs because your lambda references objects that aren't Serializable.
at org.jinq.jpa.transform.LambdaInfo.analyze(LambdaInfo.java:33)
Do you know it there's any way to perform this operation?
BTW i had to use "indexOf" becuase "contains" was not working :(
Thanks a lot for your help and best regards,
Ricardo
`JinqJPAStreamProvider streams = new JinqJPAStreamProvider(MainApp.getEmf());
EntityManager em = MainApp.getEmf().createEntityManager();
JinqStream<SynallassomenoiEntity> stream = streams.streamAll(em, SynallassomenoiEntity.class);
String eponimia = eponTextField.getText();
if (eponimia.length() > 0) {
if (eponimiaCombo.getSelectionModel().getSelectedIndex() == 0) {
stream = stream.where(p -> p.getName().equals(eponimia));
}
if (eponimiaCombo.getSelectionModel().getSelectedIndex() == 1) {
stream = stream.where(p -> JPQL.like(p.getName(), eponimia + "%"));
}
if (eponimiaCombo.getSelectionModel().getSelectedIndex() == 2) {
stream = stream.where(p -> p.getName().contains(eponimia));
}
if (eponimiaCombo.getSelectionModel().getSelectedIndex() == 3) {
stream = stream.where(p -> JPQL.like(p.getName(), "%" + eponimia));
}
}
stream.sortedBy(p -> p.getName());
showndata = stream.toList();
`
it returns the list unsupported...
Hey
I have a super class which is extended by sub class, the super class has a Discriminator Column
and each sub class has its own Discriminator value
, Lets say that Animal is the super class
, Cat is the sub class
now if I try this
where(animal -> animal instanceof Cat)
it throws an Exception
Exception in thread "main" java.lang.IllegalArgumentException: Could not analyze lambda code
org.jinq.rebased.org.objectweb.asm.tree.analysis.AnalyzerException:
Unhandled bytecode instruction
Note that, we dont have an attribute that maps to the discriminator column, but even if we could add it and then JinQ was able pick it up and use it instead of instance of
, it will still be a work around and not a perfect solution
is there any possible way to detect inheritance ?
I think since JPA 2.0, there is a TYPE command, and I think also it can detect the discriminator column.
http://stackoverflow.com/questions/7807608/how-to-create-an-instance-of-like-query-in-jpa-2-0
That's probably safe. Also, possibly allow a select() after a sortedBy(). And when something isn't allowed, create a better error message (possibly with a URL to the chart showing what's allowed).
def customers(): JinqIterator[Customer] = { return streams.streamAll(em, classOf[Customer]); }
could be replaced by:
def customer = streams.streamAll(em, classOf[Customer]) etc. and remove the () in the uses:
customers.foreach( c => println(c.getName() + " " + c.getCountry() + " " + c.getSalary()));
(or use s"")
In JPQL we can use date functions (see this doc).
In Jinq, we can't use this because new Date()
is not recognized, and in the JPQL class there is not functions to handle this case.
What are the steps required to implement something like this? Or this is not in the scope of Jinq?
Thanks for the wonderful library.
It probably would be nice if, in addition to the ".iterator()", it would be supported a ".iterable()" that return a Iterable as result, as is possible with JPA with Spring.
Hi!
There is any way to get the Query object (before the run but after the fillQueryParameters) ? I want to call with Spring Data Pageable object to get paged result. Or JINQ have another way to create spring boot Page object? (It need the total number of result, not just the "page")
I really love the abstraction of Jinq, but the analysis of complex queries takes a lot of time compared to JPQL-Queries or native SQL queries. Would it be possible to cache Jinq queries, which have already been analyzed, or better, to have a maven/gradle/... compiler plugin precompiling Jinq into JPQL or even already SQL?
I'm not sure on how to cache queries, i.e. how to identify them; mainly just brainstorming here :)
Precompilation could for example be done with lombok. This could mean, that queries, which should be precompiled, need to be annotated. Problems here could be (a) having queries propagating through multiple functions and (b) analyzing the "settings" of the JinqJPAStreamProvider
(e.g. hints or native database function mapping (if this will be implemented)).
Hello,
I am trying to have methods inside my where statement. Here is a simplified example:
Collection<ExperimentLog> experimentLogs = experiments(entityManager)
.where(log -> test(log.getGroupid(), groupid))
.sortedDescendingBy(log -> log.getSubmittime())
.limit(20)
.toList();
And my test method looks like this one:
private boolean test(int test, int groupid) {
return test == groupid;
}
However, I get the following error:
java.lang.IllegalArgumentException: Lambda has an unknown format (an unsupported type of method handle is possibly being used here)
at org.jinq.jpa.transform.LambdaAnalysis.fullyAnalyzeLambda(LambdaAnalysis.java:176)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
My understanding of streams and lambda in java 8 is very limited. I would appreciate any help.
Thanks!
Currently one has to use Pair
or Tuple2,3,...
to compine multiple values. Would it be possible to support creating and using (immutable) POJOs?
This would be extremely helpful for One-To-One mapped joins, as no Collector
is needed to be written. It would also simplify reading huge queries, as foo.bar().baz().qux
is easier to read and keep in mind than pair.getOne().getOne().qux
.
The getting started page says that you have to download and compile Jinq to get started. It would be preferable if it let the user know that Jinq is available via Maven Central since that's how most people will want to use the library
Being able to leverage Hibernate Search alongside JPA would be great.
A possible approach would be: if all the properties involved in the lambda function have HS annotations, then generate a HS Query instead of a JPA one.
When you extend classes with more than one @MappedSuperclass, only first one getters are put in safeMethods hash, and throws error on analyzing labda expression. In my case i try to call getId which is 2nd Superclass.
Example:
class BaseEntity { private int; public int getId();}
class BaseVersion extend BaseEntity {...}
class Product extend BaseVersion {...}
Product is missing all functions from BaseEntity, reason for that is in file and line
https://github.com/my2iu/Jinq/blob/master/jinq-jpa/main/org/jinq/jpa/transform/MetamodelUtil.java#L360
you make new array, not sure why but you forget to include previous subClasses.
Change code
FROM:
List newSubclasses = new ArrayList<>();
newSubclasses.add(className);
findMetamodelEntityGetters(jpaObject, newSubclasses);
TO:
List newSubclasses = new ArrayList<>(subclassNames);
newSubclasses.add(className);
findMetamodelEntityGetters(jpaObject, newSubclasses);
Can you please add support for calling database functions with Jinq?
In JPA 2.1 Support for calling database functions was added. It can be reviewed in the JPA 2.1 Specification on Page 193:
4.6.17.3 Invocation of Predefined and User-defined Database Functions
The invocation of functions other than the built-in functions of the Java Persistence query language is supported by means of the function_invocation syntax. This includes the invocation of predefined database functions and user-defined database functions.
In eclipselink it is implemented since version 2.4, i don't know about hibernate though.
I would have the following suggestions:
.function(...)
I imagine this to be the simpler one to implement but rather hard and inflexible to use
JinqJPAStreamProvider
For example Math.ceil()
could be added and translated like this:
streams.addNativeFunction(Math::ceil, "CEIL")
streams.streamAll(em, Book.class)
.select(b -> new Pair(b.getName(), Math.ceil(b.getPrice())))
could get translated into
SELECT b.name, FUNCTION("CEIL", b.price) FROM Book b
How would this work for non-static methods? If I have the instance of an object and mapped one of this object's methods, could this case still be analyzed?
The inside of the Java or more and spring binding, relevant information display configuration?
Added, in fact, the inside of the scala slick is good enough, but Java jinq gave us the same.
Jinq currently expects boolean getters to be of the form getField() because that seemed to be the default of JPA providers. Supporting isField() requires programmers to manually register the methods with Jinq which can be annoying.
I'll have to find an example of isField() in a JPA entity first to get a feel for how different JPA providers handle them.
when I use spring boot jpa with hibernate and sqlserver2014, I met the error:
Hibernate: select note0_.id as id1_0_, note0_.body as body2_0_, note0_.title as title3_0_ from Note note0_ limit ?
2015-09-03 16:27:49.846 WARN 6924 --- [tp1673604690-19] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 102, SQLState: 42000
2015-09-03 16:27:49.847 ERROR 6924 --- [tp1673604690-19] o.h.engine.jdbc.spi.SqlExceptionHelper : “limit”附近有语法错误。
why? who can help me .By th way , my jinq version is 1.8.4.
I add below code into JinqJPAStreamProvider:
public JPAJinqStream selectPrimaryKeys(JPAJinqStream base) {
JPAQueryComposer jpaComposer = ((QueryJPAJinqStream) base).jpaComposer;
SelectFromWhere query = (SelectFromWhere) jpaComposer.query;
SelectFromWhere copy = query.shallowCopy();
org.jinq.jpa.jpqlquery.RowReader reader = copy.getRowReader();
ColumnExpressions columnExpressions = new ColumnExpressions(reader);
columnExpressions.columns.add(new ReadFieldExpression(
((SelectOnly) (copy)).cols.getOnlyColumn(), "id"));
copy.cols = columnExpressions;
return new QueryJPAJinqStream(JPAQueryComposer.findAllEntities(
metamodel, cachedQueries, lambdaAnalyzer,
jpqlQueryTransformConfigurationFactory, jpaComposer.em, hints,
copy));
}
But there is erro in "toList" when I run below code , why?
JPAJinqStream idStream =new JinqJPAStreamProvider(getEntityManager()
.getMetamodel()).selectPrimaryKeys(stream);
List idList = idStream.toList() ;
Is it possible to serialize / deserialize Jinq expressions/streams e.g. to send them over network?
Reasoning behind question: In a microservice environment, a nice way to provide data, which needs to be aggregated by several services would be to send over the abstract Jinq stream instead of the loaded objects.
Let's take the following example: Service A handles users and Service B handles some data related to users. Both share the same database. When I want to fetch the data object with it's creator, Service B fetches the data and gets the creator-id. It sends the creator ID to Service A, which then returns the user to Service B. Service B aggregates this information and returns the full data set to Me.
This results in 2 DB queries, which must be executed after each other.
My question is, whether the following scenario could also be done this way:
Service B creates a JinqStream, which will fetch the data object. It serializes the stream, sends it over to Service A, which, after deserialization, adds it's aggregate to the JinqStream based on the user's id. It serializes it again, sending it back to Service A, which after deserialization executes it.
This results in only one DB-Query and the procedure is far more abstract on a higher level.
Kind Regards,
BH16
deal.getInstrument().getName().toLowerCase().equals(instrumentNameLowerCase)
is working greatly
Unfortunately
deal.getInstrument().getName().equalsIgnoreCase(instrumentName)
is not working as expected.
Is it possible to map that equaslsIgnoreCase match SQL UPPER (string1) UPPER(string2) or same thing for lower?
like this:
.where(xxxx)
.select(m -> new UserWalletModel(m.getId(), m.getName(), m.getWallet().getBalance()))
.toList();
as like HQL:
.createQuery("select new com.yzx.discover.model.UserWalletModel(u.id, u.name,u.wallet.balance) " +
"from UserEntity u " +
"where u.wallet.balance > :balance")
.setParameter("balance", balance)
.list();
I use the eclipselink-static-weave-plugin during compile time for my entities. For relations between classes I use the annotations OneToOne
,OneToMany
and ManyToOne
. If I use eager loading, everything will work as expected and queries execute normally. Using FetchType.LAZY
on OneToMany
queries also works as expected. But using lazy loading together with OneToOne
or ManyToOne
relational annotations, then using the field's getter-methods inside queries, I get the following exception: org.jinq.rebased.org.objectweb.asm.tree.analysis.AnalyzerException: Unknown method Sale:getCustomer()LCustomer; encountered
.
These are my entities:
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name="CUSTOMER_ID")
private List<Sale> sales;
}
@Entity
@AllArgsConstructor
@NoArgsConstructor
@Data
public class Sale {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
private Customer customer;
}
The following code (joining OneToMany
) works as expected:
streams.streamAll(em, Customer.class)
.join(c -> JinqStream.from(c.getSales()))
.forEach(System.out::println);
The following one, working with ManyToOne
, produces the issue:
streams.streamAll(em, Sale.class)
.joinFetch(s -> JinqStream.of(s.getCustomer()))
.forEach(System.out::println);
It results in
java.lang.reflect.InvocationTargetException
Caused by: java.lang.IllegalArgumentException: Could not analyze lambda code
Caused by: org.jinq.rebased.org.objectweb.asm.tree.analysis.AnalyzerException: Unknown method Sale:getCustomer()LCustomer; encountered
Interestingly, in the decompiled class files, the method getCustomer
does exist:
public Customer getCustomer() {
return this._persistence_get_customer();
}
My code is below:
streams.streamAll(em, typeof(Customer))
.where[o|o.name=="Bob"].toList()
.forEach[println(it.name)]
xtend translate the code to below:
JPAJinqStream _streamAll = SampleMain.streams.streamAll(this.em, Customer.class);
final JinqStream.Where<Customer, Exception> _function = (Customer o) -> {
String _name = o.getName();
return Objects.equal(_name, "Bob");
};
JPAJinqStream _where = _streamAll.where(_function);
List _list = _where.toList();
final Consumer _function_1 = (Customer it) -> {
String _name = it.getName();
InputOutput.println(_name);
};
_list.forEach(_function_1);
but it generate erros:
Exception in thread "main" java.lang.IllegalArgumentException: Could not analyze lambda code
at org.jinq.jpa.transform.LambdaAnalysis.fullyAnalyzeLambda(LambdaAnalysis.java:197)
at org.jinq.jpa.transform.LambdaInfo.fullyAnalyze(LambdaInfo.java:116)
at org.jinq.jpa.JPAQueryComposer.applyTransformWithLambda(JPAQueryComposer.java:293)
at org.jinq.jpa.JPAQueryComposer.where(JPAQueryComposer.java:379)
at org.jinq.jpa.JPAQueryComposer.where(JPAQueryComposer.java:1)
at org.jinq.orm.stream.QueryJinqStream.where(QueryJinqStream.java:45)
at org.jinq.jpa.QueryJPAJinqStream.where(QueryJPAJinqStream.java:87)
at SampleMain.runSampleQueries(SampleMain.java:79)
at SampleMain.main(SampleMain.java:52)
Caused by: org.jinq.rebased.org.objectweb.asm.tree.analysis.AnalyzerException: Unknown static method com/google/common/base/Objects:equal(Ljava/lang/Object;Ljava/lang/Object;)Z encountered
at ch.epfl.labos.iu.orm.queryll2.symbolic.BasicSymbolicInterpreter.naryOperation(BasicSymbolicInterpreter.java:385)
at org.jinq.rebased.org.objectweb.asm.tree.analysis.Frame.execute(Unknown Source)
at ch.epfl.labos.iu.orm.queryll2.path.CodePath.calculateReturnValueAndConditions(CodePath.java:148)
at ch.epfl.labos.iu.orm.queryll2.path.TransformationClassAnalyzer.analyzeMethod(TransformationClassAnalyzer.java:440)
at ch.epfl.labos.iu.orm.queryll2.path.TransformationClassAnalyzer.analyzeLambdaMethod(TransformationClassAnalyzer.java:406)
at org.jinq.jpa.transform.LambdaAnalysis.analyzeLambda(LambdaAnalysis.java:318)
at org.jinq.jpa.transform.LambdaAnalysis.fullyAnalyzeLambda(LambdaAnalysis.java:187)
... 8 more
Hi!
Possible to create leftOuterJoin with JoinWithSource implementation?
I have 3 table.
I18NMaster <-> I18N <-> Language
I want to select all I18NMaster field in all Language, and where exists, I want the I18N localized text...
b0c1
Currently only equals seems to be supported, I guess it is not that hard for the above methods to be added
Unlike the current approach of sorting by one field and then the other, this syntax will list the sort fields in the opposite order, which some people find less confusing.
Is possible / feasible to implement crossJoin
in a way that allows passing parameters while cross-joining?
Given these methods:
public JPAJinqStream<Customer> getCustomer(long id) {
return streams.streamAll(em, Customer.class)
.where(c -> c.getId() == id);
}
public JPAJinqStream<Sale> getSales(List<Long> ids) {
return streams.streamAll(em, Sale.class)
.where(s -> JPQL.isInList(s.getId(), ids));
}
Would it be possible to implement a crossJoin
taking a lambda which takes the current type as parameter and returns the new stream to join with like this?
getCustomer(1337)
.crossJoin(c -> getSales(c.getSales()));
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.