Grails - Query list of items from multiple domains - mongodb

Working on grails 3 application. My domain structure is defined below:
Job{
Integer id
String title
Date publishedDate
}
JobType{
Integer id
String name
}
JobJobTypeMap{
String jobId
String jobTypeId
}
For example,
**Job**
id title
1 job1
2 job2
**JobType**
id name
1 govt
2 private
**JobJobTypeMap**
jobId jobTypeId
1 1
1 2
2 2
I need to get list of jobs (offset and max attributes and order published date descending) with particular jobType.
Kindly, don't ask me to change the domain structure.
Any suggestions would be appreciated

You are not defining properly the domain classes. You don't have to create the relation class between Job and JobType, Grails will do it automatically on you DB.
Job{
Integer id
String title
Date publishedDate
JobType jobType
}
JobType{
Integer id
String name
}
List<Job> jobs = Job.findAllByJobType(jobTypeInstance, [sort: '', order:'', max: '', offset: ''])

You have to use Grails SQL/HSQL to query the DB.
In controller
def getList() {
def params = [column:"job.title", order:'DESC', offset:0, limit:5]
Sql sql = new Sql(dataSource)
String query = """
SELECT job_job_type_map.job_id jobId, job_job_type_map.job_type_id jobTypeId,
job.title jobTitle, jobType.name jobTypeName
FROM job job, job_type jobType, job_job_type_map job_job_type_map
WHERE job_job_type_map.job_id = job.id
AND job_job_type_map.job_type_id = jobType.id
ORDER BY ${params.column} ${params.order}
LIMIT :limit
OFFSET :offset
"""
List<GroovyRowResult> result = sql.rows(query, params)
result.each {
println "${it.jobId} ${it.jobTypeId} ${it.jobTitle} ${it.jobTypeName}"
}
render template: 'list', model: [jobRows:result]
}
in GSP
<g:each in="${jobRows}" var="job">
"${job.jobId} ${job.jobTypeId} ${job.jobTitle} ${job.jobTypeName}" <br/>
</g:each>
Be careful ORDER BY ${params.column} ${params.order}
There is restriction in using named parameter in some places. You can find here
Enjoy!

If you do not want to change domain structure and do not want to write SQL/HSQL then you can use this approach:
def jobTypeId = JobType.findByName(params.jobTypeName).id
def jobIdList = JobJobTypeMap.findAllByJobTypeId(jobTypeId)?.jobId
def jobList = Job.createCriteria().list(max:max, offset:offset) {
'in'("id", jobIdList)
order("publishedDate", "desc")
}
jobList will give you the list of all jobs with specific job type, max, offset and order by publishedDate.

Related

How can i ignore: PSQLException: The column name clothStyle was not found in this ResultSet

I created a a query to only get 4 items from a row in a table which does not include the column cloth style, so i understand why i get the error, but how can i tell Spring Jpa or JPA it is on purpose. and i just want the id, name and color table ?
this is my code:
#RequestMapping(value = "/query/material",method = RequestMethod.GET)
public String QueryMaterialTable(HttpServletRequest request){
DataTableRequest<Material> dataTableInRQ = new DataTableRequest<Material>(request);
PaginationCriteria pagination = dataTableInRQ.getPaginationRequest();
String baseQuery = "SELECT id as id, time as time, name as name, color as color, price as price, (SELECT COUNT(1) FROM MATERIAL) AS totalrecords FROM MATERIAL";
String paginatedQuery = AppUtil.buildPaginatedQuery(baseQuery, pagination);
System.out.println(paginatedQuery);
Query query = entityManager.createNativeQuery(paginatedQuery, Material.class);
#SuppressWarnings("unchecked")
List<Material> materialList = query.getResultList();
DataTableResults<Material> dataTableResult = new DataTableResults<Material>();
dataTableResult.setDraw(dataTableInRQ.getDraw());
dataTableResult.setListOfDataObjects(materialList);
if (!AppUtil.isObjectEmpty(materialList)) {
dataTableResult.setRecordsTotal(String.valueOf(materialList.size())
);
if (dataTableInRQ.getPaginationRequest().isFilterByEmpty()) {
dataTableResult.setRecordsFiltered(String.valueOf(materialList.size()));
} else {
dataTableResult.setRecordsFiltered(String.valueOf(materialList.size()));
}
}
return new Gson().toJson(dataTableResult);
}
If I got the question right, your problem is with the following two lines:
Query query = entityManager.createNativeQuery(paginatedQuery, Material.class);
List<Material> materialList = query.getResultList();
You have various options to fix this:
provide a complete column list, i.e. provide the missing column in the SQL statement and just make them NULL;
Don't use Material but a new class that has the matching attributes.
Don't use a native query but JPQL and a constructor expression.
Use a ResultTransformer.
Use Spring Data and a Projection.
Use a Spring JdbcTemplate.

Relationship in Laravel-5.6

I have three tables like below.
posts
_id - integer
name - string
sentences
_id - integer
post_id - integer
name - string
translations (word by word)
_id - integer
post_id - integer
sentence_id - integer
word_id - integer
name - string
In PostController.php I am trying to fetch data like below
return Post::with(['sentences', 'sentences.translations'])->limit(2)->get();
I have function in post.php model is like below
protected $primaryKey = '_id';
public function sentences()
{
return $this->hasMany('App\Model\sentence', 'post_id','_id');
}
I have function in sentences.php model is like below
protected $primaryKey = '_id';
public function translations()
{
return $this->hasMany('App\Model\translation', 'sentence_id','_id');
}
I would like to fetch posts along with sentences and translations.
I can fetch post and sentences but I am facing issue while I am trying to fetch translations.
I am getting all the translations which sentence_id is matched with idof sentences table, but post_id is not matching with the current post id of post table.
If you've named your primary keys any thing other than id, you need to set the primary key name on each model:
class Post extends Model {
protected $primaryKey = '_id';
}
Match your relations to the correct name also:
return $this->hasMany(Sentence::class, 'post_id','_id');

Retrieve specific table rows using Java Persistence

I have a table patient_details(patient_id, first_name, last_name, address,date_of_birth, gender, contact_number,occupation). I have generated an entity class and a PersistenceUnit. I can only find an object using its ID:
PatientDetails pd = em.find(PatientDetails.class,patient_id);
I want to know how to find an object by using other column name(s) instead of just the primary key.
Have a look on JPQL or JPA Critera.
https://docs.oracle.com/html/E24396_01/ejb3_langref.html
http://docs.oracle.com/javaee/6/tutorial/doc/gjitv.html
Execute Querys:
http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#createQuery%28javax.persistence.criteria.CriteriaQuery%29
For example:
if
class PatientDetails
String first_name;
#Column(",date_of_birth")
Date birth;
...
}
then
String sql = "SELECT p FROM PatientDetails p where p.first_name = :fname and p.birth > :generation";
Date generation = new Date(int 1980, 0, 1);
TypedQuery<PatientDetails> query = EM.createQuery(sql);
query.setParameter("fname","John");
query.setParameter("generation",generation);
return query.getResultList
returns patients called John and born after 1980.
But you should read the links recommended by #pL4Gu33

Combinations of Where Criteria - Still parameterized query - Dapper

I have a Dapper query as follows
Public void GetAllCusomers(string CustmoerId, StringFirstName, String LastName, String Gender)
{
TblCustomer tblCustomer = new TblCustomer();
using (var sqlConnection = new SqlConnection(“DatabaseConncetionString"))
{
sqlConnection.Open();
//tblCustomer = sqlConnection.Query<TblCustomer >("SELECT * FROM tblCustomer WHERE CustomerId = #CustomerID" AND FirstName = #FirstName……………, new { CustomerID = CustomerId,……………. }).ToList();
tblCustomer = sqlConnection.Query<TblCustomer >("SELECT * FROM tblCustomer WHERE CustomerId = #CustomerID", new { CustomerID = CustomerId }).ToList();
sqlConnection.Close();
}
}
The question is how to build the query? In the above method user can provide value to any parameters that he wishes to query. If the parameter value is blank that will not be used in the WHERE criteria. I will be using all the supplied parameters in the where criteria with AND operations.
Without Dapper it is easy to build the dynamic query by concatenating the SQL statement depending upon the supplied parameters. How to build these queries in Dapper without compromising the parameterized feature.
Thank you,
Ganesh
string sql = "SELECT * FROM tblCustomer " +
"WHERE CustomerId = #CustomerID AND FirstName = #FirstName"; // ...
var parameters = new DynamicParameters();
parameters.Add("CustomerId", customerID);
parameters.Add("FirstName", firstName);
// ...
connection.Execute(sql, parameters);
You would do it similar to how you build a dynamic query. Build your string dynamically (based on user input), only including filters in the Where clause as needed.
Exmpale:
var query = new StringBuilder("select * from users where ");
if(!string.IsNullOrEmpty(firstname)) query.Append("FirstName = #FirstName ");
As far as passing in the parameters, you can either construct an object that includes all of your possible parameters with values to pass in:
new {FirstName = "John", LastName = "Doe"}
or, if you only want to pass in parameters that will actually be used, you can build a Dictionary<string,object> that contains only those parameters you need to pass in:
new Dictionary<string,object> { {"FirstName", "John" } }

JPA: Selecting entities based on multiple criterions on multiple child entities

I have a problem getting the following scenario to work. A student can take tests. A student have over time taken a few tests and got a score for each test. Each student entity have a list of tests that they have completed mapped as #OneToMany.
Now I want to select all students that have completed tests on a range of grouped criterions. I want for example to search for all students that have:
Group 1: Completed "Test 1" and got a score "between 75 and 100"
and/or
Group 2: Completed "Test 2" and got a score "between 50 and 80"
This is what I have so far but it does not do what I need (cannot search by multiple parameters meaning that I have to perform the query multiple times):
SELECT s FROM Student s JOIN s.tests t WHERE t.score BETWEEN :minScore AND :maxScore AND t.testName = :testName
Is there a way to use a single NamedQuery to achieve what I want? To retrieve all Students that have completed a test that matches at least one of the parameter groups above? I've been experimenting with joins but keep running into the wall.
I made a sample code skeleton below to illustrate what I'm trying to do.
#Entity
#NamedQueries({
#NamedQuery(name="Student.findStudentByParams", query="????????") // What should this query look like to satisfy the criteria? (see below for more detail)
})
public class Student {
// .. Some other variables that are not relevant for this example
#Id
private String name;
#OneToMany(fetch=FetchType.EAGER, mappedBy = "student")
private List<Test> tests;
// Setters and getters
}
#Entity
public class Test {
private double score;
private String testName;
// .. Some other variables that are not relevant for this example
#ManyToOne(cascade=CascadeType.ALL)
private Student student;
// Setters and getters
}
public class SearchParameters {
private double minScore;
private double maxScore;
private String testName;
public SearchParameters(String minScore, String maxScore, String testName) {
this.minScore = minScore;
this.maxScore = maxScore;
this.testName = testName;
}
// Setters and getters
}
public class MainClass {
public static List<Student> getStudents(List<SearchParameters> searchParams) {
// Database initialization stuff
// What should the query look like to find all students that match any of the combined requirements in the searchParams list?
// Is it possible to do in a single query or should i make multiple ones?
// What parameters should i set? Is it possible to put in the entire array and do some sort of join?
// Retrieve all students which matches any of these search parameters:
// Have either:
// Completed "Test 1" and got a score between 75 and 100
// and/or:
// Completed "Test 2" and got a score between 50 and 80
Query namedQuery = em.createNamedQuery("Student.findStudentByParams");
namedQuery.setParameter(??);
return (List<Student>)namedQuery.getResultList();
}
public static void main() {
List<SearchParams> searchParams = new ArrayList<SearchParams();
searchParams.add(new SearchParameters(75,100, "Test 1"));
searchParams.add(new SearchParameters(50,80, "Test 2"));
// Retrieve all students which matches any of these search parameters:
// Have either:
// Completed "Test 1" and got a score between 75 and 100
// and/or:
// Completed "Test 2" and got a score between 50 and 80
ArrayList<Student> students = getStudents(searchParams);
for(Student s: students) // Print all user that match the criteria
{
System.out.println("Name: " + s.getName());
}
}
}
You need to use Criteria Builder (and eventually the canonical Metamodel).
Try something like this (code not tested):
EntityManager em; // put here your EntityManager instance
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Student> cq = cb.createQuery(Student.class);
Root<Student> student = cq.from(Student.class);
Predicate predicate = cb.disjunction();
for (SearchParams param : searchParams) {
ListJoin<Student, Test> tests = student.join(Student_.tests);
Predicate tempPredicate1 = cb.equal(tests.get(Test_.testName), param.getTestName());
Predicate tempPredicate2 = cb.ge(tests.get(Test_.score), param.getMinScore());
Predicate tempPredicate3 = cb.le(tests.get(Test_.score), param.getMaxScore());
Predicate tempPredicate = cb.and(tempPredicate1, tempPredicate2, tempPredicate3);
predicate = cb.or(predicate, tempPredicate);
}
cq.where(predicate);
TypedQuery<Student> tq = em.createQuery(cq);
return tq.getResultList();
I don't see how it would be possible without composing the query dynamically. Consider using the Criteria API to create it.
I would design the query like this:
select s from Student s where
exists (select t.id from Test t where t.student.id = s.id and ...)
or
exists (select t.id from Test t where t.student.id = s.id and ...)
or
exists (...)
As you see, there's a repeating pattern, and all these subqueries are similar an are combined into a disjunction.