So I got many to many relationship in two different entities. One has a list of the other and the other has a list of the first one. My question is how to make query that takes one field of the entity + the collection. For example Professors and Projects are related many to many, how to get the name of all professors + the projects they are involved in? Please if you have ideas share deeply and broadly. Thanks
You can use JP QL Projections, select on the Professor's name plus the properties of the Project entity associated with the professor. Using your example:
#Entity
public class Professor {
private Long id;
private String name;
private Collection<Projects> projects;
...
}
#Entity
public class Project {
private Long id;
private String status;
private Collection<Professor> professors;
...
}
Sample data:
Your Professor table has 2 rows:
1 Mark
2 John
Your Project table has 4 rows:
1 A DELAYED
2 B COMPLETED
3 C ON-TRACK
4 D COMPLETED
The JOIN table (the first column is the Professor ID, 2nd column is the Project ID):
1 1
1 2
2 3
2 4
Your JPA Query will be like:
SELECT prof.name, proj.name, proj.status FROM Professor prof JOIN prof.projects proj
Then the results will be:
Mark A DELAYED
Mark B COMPLETED
John C ON-TRACK
John D COMPLETED
However, if you're trying use projection on the Professor name plus its project collections, that will not be possible in JP QL. JP QL only allows single-valued path expression in the SELECT clause.
Example: This is NOT A VALID JP QL Query, since prof.projects is a collection-valued path expression.
SELECT prof.name, prof.projects FROM Professor prof
Related
I have 4 tables
Student, Result, ResultDetail and Queue
Result and ResultDetails are related with ResultID as ForeignKey.
Student and Result are related with StudentID as ForeignKey
and Student can have 'N' number of results but will have only one active Result.
Queue and Student are related with StudentID.
Now using Entity Framework and Repository Pattern,
I want to
Perform ->
Queue q join Student s on q.StudentID = s.StudentID
join Result r on q.StudentId = r.StudentID
Join ResultDetail rd on t.ResultID = rd.ResultId where r.IsActive = 1
Could some one please help in achieving this with Repository Pattern?
Do we need to create 4 Repositories even though we have relations between tables.
Performance is also a criteria .
Thanks in Advance.
Could some one please help in achieving this with Repository Pattern?
Yes. Create a custom subtype of DbContext to serve as your Repository. Add a DbSet Property for each of your entity types. Also put Navigation Properties on your Entities so your query so you don't have to use join in LINQ.
I am trying to design a specific part of a domain model for a website.
The issue I have is representing a sort of availability matrix with days of the week (Day) and time slots (TimeSlot).
Possible values for Day are:
Monday
Tuesday
Wednesday
...
Possible values for TimeSlot are:
Before school
Morning
Noon
Afternoon
After school
Evening
Nighttime
Currently, I have a JPA entity with the two above enums (Day and TimeSlot) as fields:
#Entity
public class DayToTimeSlot {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#NotNull(groups = { Default.class, Validation.AdvertisementCreation.class })
#Enumerated
private Day day;
#NotNull(groups = { Default.class, Validation.AdvertisementCreation.class })
#Enumerated
private TimeSlot timeSlot;
}
I then populate a DayToTimeSlot reference table with all possible values/combinations.
And finally, I have an Advertisement entity that has a #ManyToMany java.util.Set of DayToTimeSlot instances as follows:
#ManyToMany
private Set<DayToTimeSlot> dayToTimeSlots;
This results in a advertisement_day_to_time_slots join table which can grow very quickly.
Is there not a better way to model this availability matrix?
I suppose you have only 7Days * 7 TimeSlots = 49 entries in the DayToTimeSlot table and then you only reference them in the Advertisement table.
To remain fully flexible I don't see any better ways to model the availability matrix. But your problem seems to be the association table. I would put myself the following questions: how quickly does it grow? Will you reach 1million in the next 5 years? To search in such 1 million-entries table is not such a big problem.
Independently of your answer to the last question you could think of caching the DayToTimeSlot table.
Besides, if you really do not want to search Advertisment by DayToTimeSlot, you could save the associations as a String field in Advertisment and load them when the getter getDayToTimeSlots() is called. E.g. On save of the Advertisment you itrate through the whole set of DayToTimeSlot, ad build a string of form : "MONDAY_BEFORE_SCHOOL, TUESDAY_NOON,..." and save it. On loading of an Advertisment you process the string in order to create the Set. Of course, in this case you will delete the #ManyToMany reference.
But another feasible solution would be the following (if for every week day you have a single entry of TimeSlot):
If you want to search for Advertisment only by day, or if you don't mind writing a bit more complex queries, then you could to add 7 new fields: mondayAvailability, tuesdayAvailability, to the Advertisment entity, save the data there and get rid of the #ManyToMany relationship..
Lets say I have 3 entities:
Car
Place
Visit
A Visit represents the occurrence when a Car visited a Place, so it would have the time it arrived, the time it left, and 2 foreign keys, one to the Car and one to the Place.
In JPA, Visit has #ManyToOne relationship to Car and #ManyToOne relationship to Place. (So a Car can make many visits and a Place can have many visits), using the foreign keys above.
Lets say I wanted to know the list of (unique) Cars that visited all Places (or a number of Places, doesn't matter), what is the best approach to get a Map<Place, List<Car>> so that it is only done in one SQL query?
In normal native SQL we would just use join and group by, is there some JPA trick that can achieve this elegantly?
I am using JPA 2.0 (Hibernate 4.1).
With the help of Guava (but you could easily do without):
String jpql = "select distinct place, car from Visit visit"
+ " inner join fetch visit.car car"
+ " inner join fetch visit.place place"
List<Object[]> placeAndCars = em.createQuery(jpql).getResultList();
ListMultimap<Place, Car> result = ArrayListMultimap.create();
for (Object[] row : placesAndCars) {
result.put((Place) row[0], (Car) row[1]);
}
return result;
I'm continuing exploring Cassandra and I would like to create Student <=> Course relation which is similar to Many-to-Many on RDBMS.
In term of Queries I will use the following query;
Retrieve all courses in which student enrolled.
Retrieve all students enrolled in specific course.
Let's say that I create to Column Families. one for Course and another for Student.
CREATE COLUMN FAMILY student with comparator = UTF8Type AND key_validation_class=UTF8Type and column_metadata=[
{column_name:firstname,validation_class:UTF8Type}
{column_name:lastname,validation_class:UTF8Type}
{column_name:gender,validation_class:UTF8Type}];
CREATE COLUMN FAMILY course with comparator = UTF8Type AND key_validation_class=UTF8Type and column_metadata=[
{column_name:name,validation_class:UTF8Type}
{column_name:description,validation_class:UTF8Type}
{column_name:lecturer,validation_class:UTF8Type}
{column_name:assistant,validation_class:UTF8Type}];
Now how should I move on?
Should I create third Column Family with courseID:studentId CompisiteKey? if yes, Can I use Hector to query by only one (left or right) Composite key component?
Please help.
Update:
Following the suggestion I created the following Schema:
For Student:
CREATE COLUMN FAMILY student with comparator = UTF8Type and key_validation_class=UTF8Type and default_validation_class=UTF8Type;
and then we will add some data:
set student['student.1']['firstName']='Danny'
set student['student.1']['lastName']='Lesnik'
set student['student.1']['course.1']=''
set student['student.1']['course.2']='';
Create column Family for Course:
CREATE COLUMN FAMILY course with comparator = UTF8Type and key_validation_class=UTF8Type and default_validation_class=UTF8Type;
add some data:
set course['course.1']['name'] ='History'
set course['course.1']['description'] ='History Course'
set course['course.1']['name'] ='Algebra'
set course['course.1']['description'] ='Algebra Course'
and Finally Student In Course:
CREATE COLUMN FAMILY StudentInCourse with comparator = UTF8Type and key_validation_class=UTF8Type and default_validation_class=UTF8Type;
add data:
set StudentInCourse['studentIncourse.1']['student.1'] ='';
set StudentInCourse['studentIncourse.2']['student.1'] ='';
I defined a data model below but it is easier to decribe the object model first and then dive into the row model, so from PlayOrm's perspective you would have
public class Student {
#NoSqlId
private String id;
private String firstName;
private String lastName;
#ManyToMany
private List<Course> courses = new ArrayList(); //constructing avoids nullpointers
}
public class Course {
#NoSqlId
private String id;
private String name;
private String description
#ManyToOne
private Lecturer lecturer;
#ManyToMany
private CursorToMany students = new CursorToManyImpl();
}
I could have used List in course but I was concerned I may get OutOfMemory if too many students take a course over years and years and years. NOW, let's jump to what PlayOrm does and you can do something similar if you like
A single student row would look like so
rowKey(the id in above entity) = firstName='dean',
lastName='hiller' courses.rowkey56=null, courses.78=null, courses.98=null, courses.101=null
This is the wide row where we have many columns with the name 'fieldname' and 'rowkey to actual course'
The Course row is a bit more interesting....because the user thinks loading al the Students for a single course could cause out of memory, he uses a cursor which only loads 500 at a time as you loop over it.
There are two rows backing the Course in this case that PlayOrm will have. Sooo, let's take our user row above and he was in course rowkey56 so let's describe that course
rowkey56 = name='coursename', description='somedesc', lecturer='rowkey89ToLecturer'
Then, there is another row in the some index table for the students(it is a very wide row so supports up to millions of students)
indexrowForrowkey56InCourse = student34.56, student39.56, student.23.56....
into the millions of students
If you want a course to have more than millions of students though, then you need to think about partitioning whether you use playOrm or not. PlayOrm does partitioning for you if you need though.
NOTE: If you don't know hibernate or JPA, when you load the above Student, it loads a proxy list so if you start looping over the courses, it then goes back to the noSQL store and loads the Courses so you don't have to ;).
In the case of Course, it loads a proxy Lecturer that is not filled in until you access a property field like lecturer.getName(). If you call lecturer.getId(), it doesn't need to load the lecturer since it already has that from the Course row.
EDIT(more detail): PlayOrm has 3 index tables Decimal(stores double, float, etc and BigDecimal), Integer(long, short, etc and BigInteger and boolean), and String index tables. When you use CursorToMany, it uses one of those tables depending on the FK type of key. It also uses those tables for it's Scalable-SQL language. The reason it uses a separate row on CursorToMany is just so clients don't get OutOfMemory on reading a row in as the toMany could have one million FK's in it in some cases. CursorToMany then reads in batches from that index row.
later,
Dean
I have an entity that has a relationship with another entity. I am able to search on columns that are in the main entity, and include columns from the relationship entity. But I need to be able to filter the list (search) on columns that are not in the relationship entity.
for example
the Invoice Entity contains a customerId property, and is related to the Customer Entity which contains the customerName property
I need to be able to search / filter the grid by customerName.
I am new to entity framework, please help.
thanks
Carl
Your relation is 1->1. In these cases I usually return a custom class to the grid that has all the columns I need, including joins with other tables.
So basically what you need is to create a custom linq query with your resultset.
The mais query should follow this example:
var q = from i in ctx.Invoices
join c in ctx.Customers on i.CustomerID equals c.CustomerID
select new{InvoiceID=i.InvoiceID, InvoiceDate=i.Date, CustomerName=c.Name};
Now, assuming we receive a CustomerName variable with the string to filter by c.Name we could do:
if(!string.IsNullOrEmpty(CustomerName))
{
q = q.where(c => c.Name.ToLower().Contains(CustomerName.ToLower()));
}
Notice that I'm performing a ToLower() operation and a Contains, this will beahave as a LIKE ingnoring case sensitivity and searching for the string anywhere in the Customer Name.
At the end you'll return the q.ToList() serialized for the jqGrid...