I would like to run multiple queries then show results in a page such as :
https://adminlte.io/themes/v3/index.html
I create a first controller query :
package controllers;
import models.Sysuser;
import play.mvc.Controller;
import play.mvc.Result;
import play.mvc.Security;
import views.html.sitemap.index;
import javax.inject.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import play.libs.concurrent.HttpExecutionContext;
import static java.util.concurrent.CompletableFuture.supplyAsync;
import play.db.*;
import io.ebean.*;
import play.Logger;
import java.util.List;
import java.util.ArrayList;
import models.LocationExtractedData;
#Security.Authenticated(Secured.class)
public class SiteMap extends Controller {
private Database db;
private final HttpExecutionContext httpExecutionContext;
private static final Logger.ALogger logger = Logger.of(SiteMap.class);
#Inject
public SiteMap(Database db,
HttpExecutionContext httpExecutionContext) {
this.db = db;
this.httpExecutionContext = httpExecutionContext;
}
public CompletionStage<Result> index() {
return SearchSomething().thenApplyAsync((List<LocationExtractedData> infos) -> {
return ok(views.html.sitemap.index.render( Sysuser.findByUserName(request().username()), infos) );
}, httpExecutionContext.current());
}
public CompletionStage<List<LocationExtractedData>> SearchSomething() {
return CompletableFuture.supplyAsync(() -> {
return db.withConnection(
connection -> {
// Imagines this is a complexe QUERY (Later in the future...)
final String sql = "SELECT sysuser_id, role_id "
+"from sysuser_role "
+"where sysuser_id = '1' "
+"and role_id in ('1','2','3','4','5') ";
final RawSql rawSql = RawSqlBuilder.parse(sql).create();
Query<LocationExtractedData> query = Ebean.find(LocationExtractedData.class);
query.setRawSql(rawSql);
List<LocationExtractedData> list = query.findList();
return list;
});
}, httpExecutionContext.current());
}
}
Can you telling me how to run multiple and optimized queries in the same time for my page full of dashboards, charts and tables!
If i create multiple list of ebeanLists ( queries ), does this will affect the loading of my page ?
IF not, then, what should i do ?
Thank you in advance,
Typically, in an application similar to the link you have provided, you create reusable APIs following the MVC design pattern. Querying the database from the controller is very much against that pattern.
Each API should be atomic, creating a single API to run 1 query to fetch all of the data for that page is not the correct approach.
If you are looking for performance for your API you should get familiar with asynchronous programming. Running your APIs async will allow your back end to process multiple front end requests at the same time, greatly improving performance.
Related
Lets assume we have one table/View with below columns
columns : ID , Status
I am writing one search API to get Data from backend
I am using org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
.....
#Repository
#Transactional
public interface UIDashboard extends JpaRepository<UIDashboardView, String> {
....
#Query(value = "SELECT results from SearchView results where "
+ "(:status is null or results.STATUS = :status) and"
+ "(coalesce(:ids) is null or results.ID in :ids)"
...
My DTO
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import java.util.Optional;
import lombok.Data;
#Data
#JsonInclude(Include.NON_NULL)
public class SearchInputDTO {
#JsonProperty(value="status",required = false)
private String status;
#JsonProperty(value="ids",required = false)
//private Optional<List<String>> ids;
//ArrayList<String> ids;
private List<String> ids;
REST API
import org.springframework.web.bind.annotation.PostMapping;
...
#PostMapping("/searchTest")
public ResponseEntity<List<ResultsDTO>> search_test(
#RequestBody #NotNull #Valid SearchInputDTO payload, HttpServletRequest request)
throws IOException {
}
{
"status" : "COMPLETED",
"ids" : [null,null]
}
Issue with the approach is to invoke POST REST API i need to send two null values [null,null] in payload i,e without id's property i am unable to execute POST API , so either i need to send null values or set in DTO.
I also tried to make Optional in DTO or
Used coalesce -> (coalesce(:ids) is null or results.ID in :ids)
i dont want to explicitly send null values in Payload , User can search with any parameter for example only with one property :status:
{
"status" : "INPROCESS"
}
Above payload not working as i am not sending "ids"
Then i tried set nulls in setter as workaround
#JsonProperty(value="ids",required = false)
private List<String> ids = new ArrayList<>(Arrays.asList(null, null));
#JsonSetter("ids")
public void setIds(List<String> li){
if (li != null) {
if (li.size() < 2){
li.add(null);
li.add(null);
this.ids = li;
}
else{
this.ids = li;
}
}
}
I dont like this work around , is there any better solution or changes required to JPQL IN parameter or SearchInputDTO .
I also followed blog Spring Data repository with empty IN clause
Thanks,
Showkath.
In a Spring Boot application, we're already having a fully functional GraphQL endpoint, serving .graphqls files via GraphQL Java Tools (we included the graphql-spring-boot-starter dependency) and handling the data resolution through our base Query class implementing GraphQLQueryResolver and subsequent GraphQLResolver's.
For a business need, we have to re-create standard REST API endpoints, so I wonder why not just making calls to GraphQL (instead of having to re-implement "by hand" the data resolution once again)?
And as it's in the same backend application, no need to make HTTP or servlet (ForwardRequest) calls, just call some API's in Java.
The thing is I don't know how to proceed.
I read this example, but it's with basic GraphQL Java (not Tools):
https://www.graphql-java.com/documentation/v9/execution/
I know this should possible because we are allowed to do this in tests:
https://github.com/graphql-java-kickstart/graphql-spring-boot/blob/master/example-graphql-tools/src/test/java/com/graphql/sample/boot/GraphQLToolsSampleApplicationTest.java
But how to do it in regular code? There is not such thing as a GraphQLTemplate.
I also tried to search through examples at:
https://github.com/graphql-java-kickstart/graphql-java-tools/tree/master/example
https://github.com/graphql-java-kickstart/graphql-spring-boot
but found nothing relevant to our need.
Found nothing more in Documentation:
https://www.graphql-java-kickstart.com/tools/
https://www.graphql-java-kickstart.com/spring-boot/
What did I miss? Ideally I'm looking to inject some GraphQLSomething like this:
#RestController
#RequestMapping(path = "api")
public class CompanyController {
#Autowired
private GraphQLSomething graphQLSomething;
#GetMapping("company/{id}")
public ResponseEntity<?> societe(#PathVariable #NotNull Integer id) {
GraphQLSomethingResult result = GraphQLSomething.query("company(id: $id) { id name andsoone }", "{ \"id\": 123456 }").execute(); // not really sure of the GraphQL syntax here, but it'll need some tests...
return result.getDataOrElse();
}
}
Finally found how to do the thing as I wanted:
import java.util.Map;
import java.util.Optional;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.google.common.collect.ImmutableMap;
import graphql.ExecutionResult;
import graphql.servlet.core.GraphQLQueryInvoker;
import graphql.servlet.core.internal.GraphQLRequest;
import graphql.servlet.input.GraphQLInvocationInputFactory;
import graphql.servlet.input.GraphQLSingleInvocationInput;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#Validated
#RestController
#RequestMapping(path = "api")
public class CompanyController {
#Autowired
private GraphQLInvocationInputFactory invocationInputFactory;
#Autowired
private GraphQLQueryInvoker queryInvoker;
#GetMapping("company/{id}")
public ResponseEntity<?> societe(#PathVariable #NotNull Integer id) {
String query = "query ($id: Int!) { company(id: $id) { id name andsoon } }";
/*
* ImmutableMap is a Guava class; you can build the map (e.g. a HashMap) on your
* own, or simply Map.to(..) in Java 9, or even #PathVariable Map<String,
* Object> variables as the method's parameter instead (but you'll miss the
* validation).
*/
Map<String, Object> variables = ImmutableMap.of("id", id);
GraphQLRequest request = new GraphQLRequest(query, variables, null);
GraphQLSingleInvocationInput invocationInput = invocationInputFactory.create(request);
ExecutionResult result = queryInvoker.query(invocationInput);
/*
* Of course result.getData() can be null here - see also result.isDataPresent()
* - but data/error handling's left to you
*/
Optional<Object> company = Optional.ofNullable(result.getData().get("company"));
return ResponseEntity.of(company);
}
}
FYI, to get the dependencies for the above code, you'll need this:
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-autoconfigure</artifactId>
<version>5.0.2</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-servlet</artifactId>
<version>6.1.3</version>
</dependency>
I want to get the SearchOutput data object by calling a query(#Query method) with natural joins of 3 tables. but when the query runs it shows an error.
I have tried to fetch the data in my spring boot controller class. But its not working because of the error
package com.example.mysqlproj.model;
import lombok.*;
public class SearchOutput {
private String hotel_name;
private String room_type;
private int price;
}
package com.example.mysqlproj.dao;
import com.example.mysqlproj.model.Room_Type;
import com.example.mysqlproj.model.SearchOutput;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.Collection;
import java.util.Date;
import java.util.List;
public interface RoomTypeDao extends CrudRepository<Room_Type,Integer> {
#Query(value="select new SearchOutput(hotel_name , room_type,(price*(?4)*(?3)*(1.15))) from Room_type natural join Hotel natural join True_contract where (?1 >= start_date and ?2 <= end_date and ?3 <=available_rooms and ?4<= max_adults )", nativeQuery = true)
List<SearchOutput[]> checkHotelList(Date from, Date to, int rooms, int adults, int total_nights);
}
The Error:
No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.example.mysqlproj.model.SearchOutput]] with root cause
My target is to fetch a searchOutput object array when the query gets called.
Are there any solutions for this. Thanks in advance
The response type from the query is
List<Map<String, Object>> .
Please change the method return type to this.
I am new to Spring Jpa and Hibernate. I am trying to fetch data using a custom function from an Oracle db. I could define an entity along with its related service, implementation and repository. In addition, I created a new custom Oracle dialect by using registerFunction as you will see below.
So I have two questions:
1) In my Oracle db, the function sits under a different schema. Do I need to specify its schema? If so how? Or will hibernate find it automatically?
I will be asking my second question at the end of this post after providing my full stacktrace...
Here is my full stack trace:
MyOracle10gDialect
package blog;
import org.hibernate.dialect.Oracle10gDialect;
import org.hibernate.dialect.function.StandardSQLFunction;
public class MyOracle10gDialect extends Oracle10gDialect {
public MyOracle10gDialect() {
super();
registerFunction("my_function", new StandardSQLFunction("my_function"));
}
}
application.properties
...
spring.jpa.database-platform=blog.MyOracle10gDialect
...
Entity:
package blog.models;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name = "item", schema = "WOS_SOURCE")
public class WosItem {
#Id
#Column(nullable = false)
private String UT;
#Column(nullable = false)
private String TI;
public String getUT() {
return UT;
}
public void setUT(String UT) {
this.UT = UT;
}
public String getTI() {
return TI;
}
public void setTI(String TI) {
this.TI = TI;
}
public WosItem(String UT, String TI) {
this.UT = UT;
this.TI = TI;
}
public WosItem() { }
#Override
public String toString() {
return "WosItem{" +
"UT='" + UT + '\'' +
", TI='" + TI + '\'' +
'}';
}
}
Service:
package blog.services;
import blog.models.WosItem;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public interface WosItemService {
List<WosItem> findAll();
WosItem findById(String id);
String find_ut(Long ut_seq);
}
Implementation:
package blog.services;
import blog.models.WosItem;
import blog.repositories.WosItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
#Service
public class WosItemServiceJpaImpl implements WosItemService {
#Autowired
private WosItemRepository wosItemRepository;
#Override
public List<WosItem> findAll() {
return this.wosItemRepository.findAll();
}
#Override
public WosItem findById(String id) {
return this.wosItemRepository.findOne(id);
}
#Override
public String find_ut(Long ut_seq) {
return this.wosItemRepository.find_ut();
}
}
Repository:
package blog.repositories;
import blog.models.WosItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
#Repository
public interface WosItemRepository extends JpaRepository<WosItem, String> {
#Query("SELECT function('my_function', input) FROM WosItem wos");
String find_ut();
}
So in my Oracle db I can use this function as shown below:
select other_schema.my_function(aa.input) from my_schema.TABLE aa;
For ex. say aa.input is 332708100009 then it returns 000332708100009
As for my second question:
2) How can I carry out this process in jpa? I am aware that my repository is not correct at all. I get an error like "Annotations are not allowed here". I could not find a way to remedy this.
Thanks in advance.
EDIT ON THROWN EXCEPTION:
Caused by: java.lang.IllegalStateException: No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode
\-[METHOD_CALL] MethodNode: 'function (my_function)'
+-[METHOD_NAME] IdentNode: 'my_function' {originalText=my_function}
\-[EXPR_LIST] SqlNode: 'exprList'
\-[NAMED_PARAM] ParameterNode: '?' {name=ut_seq, expectedType=null}
Unfortunately if you want to use the JPA 2.1 feature of the custom function call in your Select statement then you will need to perform some additional actions before you can use it.
When you use it in your where statement then it works without any additional actions, but as i wanted to use it for one of my projects inside the select just as you did then you would need to:
1) Extend the hibernate dialect and register your function(s):
package com.mypkg.dialect;
import org.hibernate.dialect.Oracle10gDialect;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.StringType;
public class CustomOracle10gDialect extends Oracle10gDialect {
public CustomOracle10gDialect() {
super();
registerFunction("my_function"
, new StandardSQLFunction("my_function", new StringType()));
}
}
2) Edit your hibernate.dialect property of your session factory to point to that custom implementation:
<property name="hibernate.dialect" value="com.mypkg.dialect.CustomOracle10gDialect"/>
Update
If the function needs to be called from a certain schema then this would be suggested:
registerFunction("my_function"
, new StandardSQLFunction("schema.my_function", new StringType()));
Further reading -> native function calls
I'm trying to use the Play framework (Java) to simply read some data from a few Oracle tables, probably even use a few complex queries later on. I'm following a tutorial but I'm having some issues retrieving the data.
My Model class look like this:
package models;
import java.util.ArrayList;
import java.util.List;
import play.libs.F;
import javax.persistence.*;
import com.avaje.ebean.*;
import play.db.ebean.*;
#Entity
#Table(name="TABLESPACE.CAT_BONDS")
public class Cat_Bond extends Model {
#Id
public String symbol;
public String database;
public String tickType;
public String assetClass;
public String sourcePlatform;
public String sourceExchange;
public static Finder<String, Cat_Bond> find = new Finder<String, Cat_Bond>(String.class,Cat_Bond.class);
public Cat_Bond(){}
public Cat_Bond(String symbol, String database, String tickType, String assetClass,
String sourcePlatform, String sourceExchange) {
this.symbol = symbol;
this.database = database;
this.tickType = tickType;
this.assetClass = assetClass;
this.sourcePlatform = sourcePlatform;
this.sourceExchange = sourceExchange;
}
/*
* retrieve all rows from the 'cat_bonds' table
*/
public static List<Cat_Bond> findAll(){
//return new ArrayList<Cat_Bond>(cat_bond);
return find.all();
}
/*
* Find by EAN
*/
public static Cat_Bond findByEan(String symbol){
return find.where().eq("symbol", symbol).findUnique();
}
}
My controller class:
package controllers;
import java.util.List;
import views.html.*;
import models.Cat_Bond;
import play.data.Form;
import play.mvc.*;
public class Cat_Bonds extends Controller {
private static final Form<Cat_Bond> cat_bondForm = Form.form(Cat_Bond.class);
public static Result list(){
List<Cat_Bond> cat_bond = Cat_Bond.findAll();
return ok(list.render(cat_bond));
}
And the application.conf entry looks like:
#Oracle
db.default.driver=oracle.jdbc.OracleDriver
db.default.url="jdbc:oracle:thin:#server.uk.net.intra:port/ALIAS"
db.default.user=user
db.default.password=pass
# Evolutions
# ~~~~~
# You can disable evolutions if needed
evolutionplugin=disabled
Problem is when the call to list is made in the controller then to findAll() in the model I get the error:
**[PersistenceException: Query threw SQLException:ORA-00904: "T0"."SOURCE_EXCHANGE": invalid identifier Bind values:[] Query was: select t0.symbol c0, t0.database c1, t0.tick_type c2, t0.asset_class c3, t0.source_platform c4, t0.source_exchange c5 from TABLESPACE.CAT_BONDS t0 ]**
#Column(name="xx")
Was required above each variable defined in the model class that was to be mapped to the table column.
You can use
clean
compile
~run
If it doesn't work properly, you can use #EntityConcurrencyMode(ConcurrencyMode.NONE) within your model class.