Course has many students and many students belong to one course. How to find to which course a particular student belong? - sql

there is a one to many unidirectional mapping from course to students
#Entity
Course {
#OnetoMany
List<Student> students;
}
#Entity
Student {
name;
age;
}
the database schema looks like
Course
id name duration fee
Student
rollno name age course_id
I am using Jpa repository for getting.
Can anyone please tell how to tell in which course a particular student belong to? Please note that i am not using bi-directioal mapping so student entity class has no reference to course.

Your mappings and DB schema are fine, and there is nothing overly special about your mapping situation - it is just a unidirectional mapping, and there is no need for force mapping it bidirectionally and/or fetching the student data if you don't need it.
JPQL: "Select c from Course c join c.students s where s.name = :name"
With spring boot
#Query("Select c from Course c join c.students s where s.name = ?1")
Course findCourseByStudentName(String name);
Your DB setup would need to be looked at to determine if the Course is unique - I'd guess it isn't, in which case you'll want to return a list of courses that might have this student name registered.

Your problem is in the database deign. No performant code will rescue you from that fate.
Typically there is a join table between Students and Courses that minimally maintains two keys - the studentId and the courseId.
Need to know the courses for a student? Select from the join table where studentId = ‘X’. Need to know students in a course? Select from the join table where courseId = ‘Y’.

I won't comment on the DB design. But solution for this problem is simple.
Basically you need to implement the relationship correctly.
#Entity
public class Course {
private Long id;
#OnetoMany(mappedBy = "course")
List<Student> students;
}
#Entity
public class Student {
private Long id;
private String name;
private Integer age;
#ManyToOne
#JoinColumn(name="course_id")
private Course course;
}
Then simply use the getter
Student student = studentRepo.findStudentById(123);
Course course = student.getCourse();
Here is the full reference: https://www.baeldung.com/hibernate-one-to-many

Related

Eclipselink Shared Cache - Lazy associations

I'm devoloping an application using JPA eclipselink 2.6.4.
I'm enabling the shared cache for serveral JPA entities.
#Entity
#Cache(
type=CacheType.SOFT,
size=500
)
public class Tutor
{
#Id
#Column(name = "id")
private String id;
#Column(name = "name")
private String name;
... getters and setters
}
#Entity
#Cache(
type=CacheType.SOFT,
size=500
)
public class Student
{
#Id
#Column(name = "id")
private String id;
#Column(name = "name")
private String name;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="TUTOR_ID")
private Tutor tutor;
... getters and setters
}
I got a typical entity association of tutor and students. It is defined as LAZY because we want to avoid the "N + 1 problem" when a students query is done.
When we execute a students query and we want the association to be resolved, we use JOIN FETCH technique:
SELECT s FROM Student s JOIN FETCH s.tutor t WHERE s.id = :id
Up here all right.
The first time we execute the JPA query, because shared cache has no students yet, a database query is executed. We get the student entity populated with the tutor entity.
Nevertheless, the second time we execute the same query, there is a hit in the shared cache, so a database query is NOT executed. But then the student entity IS NOT populated with the tutor entity.
We would like to get the student entity populated with the tutor entity.
We have seen that this is possible changing the association to EAGER, but we don't want to change to EAGER to avoid "N + 1 problem".
Any idea?
Thanks

How to update many to many relationship in Entity Framework

enter image description here
I have a many-to-many relationship between students and courses as shown.
I want to update the courses for a given student (ex student Id = 10 has selected two courses with Id 2 and 6). I want to delete course Id 6 for student id 10 and add new course, say Id = 3.
I am using a code-first approach with Entity Framework 6 in ASP.NET MVC 4.
Can anyone help to solve this issue?
enter image description here
Your specifications weren't really clear about what you meant with the term "delete a Course": do you want to remove the Course from the database, so the Course does not exist anymore? Or do you want your Student to stop following this Course?
I'll describe both, therefore I have a student with 3 Courses:
Id = 2: will not be changed; Student already followed this Course and will keep following this Course
Id = 6: student will stop following this Course; Other Students might still be following this Course
Id = 7: Course will be removed from the database
Furthermore you have:
Id = 3: Student will start following this Course
many-to-many in entity framework
If you've configured your many-to-many relation according to the Entity Framework Code First many-to-many conventions You will have something similar as the following:
class Student
{
public int Id {get; set;}
// every Student attends zero or more Courses (many-to-many)
public virtual ICollection<Course> Courses {get; set;}
... // other properties
}
class Course
{
public int Id {get; set;}
// every Course is attended by zero or more Students
public virtual ICollection<Student> Students {get; set;}
... // other properties
}
public SchoolContext : DbContext
{
public DbSet<Student> Students {get; set;}
public DbSet<Course> Courses {get; set;}
}
This is all that entity framework needs to know that you wanted to configure a many-to-many relationship between Students and Courses. Entity Framework will create the junction table with the foreign keys to the Students and Courses for you. Whenever you access the ICollections entity framework will know that a join with the junction table is needed.
This can be done without the use of Attributes or Fluent Api. These are only needed if you want to deviate from the conventions, for instance if you want different table names or column names.
Back to your problem
We have the following Ids:
int idStudent = 10;
int idCourseToStartFollowing = 3;
int idCourseToStopFollowing = 6;
int idCourseToRemoveFromDb = 7;
using (var dbContext = new SchoolContext())
{
// Fetch the Student to update (has id 10)
Student studentToUpdate = dbContext.Students.Find(idStudent);
// this student starts following Course with Id 3
Course courseToStartFollowing = dbContext.Courses.Find(idCourseToStartFollowing);
studentToUpdate.Courses.Add(courseToStartFollowing);
// this student stops following Course with Id 6
Course courseToStopFollowing = dbContext.Courses.Find(idCourseToStopFollowing);
StudentToUpdate.Courses.Remove(courseToStopFollowing);
// Remove Course with Id 7 from the database
Course courseToRemove = dbContext.Find(idCourseToRemove);
dbContext.Remove(courseToRemove);
dbContext.SaveChanges();
}
Simple comme bonjour!

How to avoid unnecessary inner join on a simple query on entity inheritance mapped with JOINED strategy?

I'm using JPA 2 with Hibernate 4.2.0-Final as provider and I have the following entities:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class Person {
#Id
private String id;
.. Person attributes ..
.. Getters/Setters ..
}
#Entity
#Table(uniqueConstraints={#UniqueConstraint(name="UniqueCode", columnNames="code")})
public class Customer extends Person {
#Column(nullable=false)
private String code;
.. Other Customer attributes ..
.. Getters/Setters ..
}
And I have the following JPQL:
SELECT count(distinct c.code) FROM Customer c
For which Hibernate is generating the following SQL:
select
count(distinct customer0_.code) as col_0_0_
from
Customer customer0_
inner join
Person customer0_1_
on customer0_.id=customer0_1_.id
But I only need to count Customers with distinct code, which happens to be a field that is specific to Customer, so there is no need for the inner join to 'Person'. I'd like Hibernate to generate the SQL like follows (i.e. without joinning table 'Person'):
select
count(distinct customer0_.code) as col_0_0_
from
Customer customer0_
Is there a way to tell Hibernate to avoid the unnecessary inner join? Maybe some Hibernate-specific Query Hint?
The JOINED Strategy in JPA uses a separate table for each class in the object hierarchy.
So if you want to load objects for a subclass you also have to load information from the parent class (As the subclass alone does not represent complete picture without the attributes in the parent class). This results in a JOIN when querying for the sub-object.
From the JPA documentation you can see this as being a main disadvantage for The JOINED strategy.
1) Aside from certain uses of the table-per-class strategy described
below, the joined strategy is often the slowest of the inheritance
models. Retrieving any subclass requires one or more database joins,
and storing subclasses requires multiple INSERT or UPDATE statements.

Complex SQL Query With Join In Liftweb

I would like to know if there is a way to do some complex SQL queries using Mapper in Liftweb.
In fact what I would like to do is to perform a Join query from databases Employes and Departments using the fact that they are linked by a 1-to-many relationship.
Another example is also welcome.
Thanks in advance.
Here are some more details: Suppose I have 2 tables :
Employee : birthday, department ID, salary
Department : department ID, budget, address
Now I would like to obtain a list of the object Employee (created with Mapper) which have a salary > 10$ and a department budget < 100$.
Of course my original code is much more complicated than that but my objective is to be able to have a List of mapped objects (ie Employee) corresponding to criterions in its own table or on a linked table.
I've looked this up. It looks as though the joins are done in the object layer.
Extrapolated from http://exploring.liftweb.net/master/index-8.html to your case:
// Accessing foreign objects
class Employee extends LongKeyedMapper[Employee] with IdPK {
...
object department extends MappedLongForeignKey(this, Department)
def departmentName =
Text("My department is " + (department.obj.map(_.name.is) openOr "Unknown"))
}
class Department ... {
...
def entries = Employee.findAll(By(Employee.department, this.id))
}
If you want to do many-to-many mappings you’ll need to provide your own
“join” class with foreign keys to both of your mapped entities.
// DepartmentId Entity
class DepartmentId extends LongKeyedMapper[DepartmentId] with IdPK {
def getSingleton = DepartmentId
object name extends MappedString(this,100)
}
object DepartmentId extends DepartmentId with LongKeyedMetaMapper[DepartmentId] {
override def fieldOrder = List(name)
}
Next, we define our join entity, as shown below.
It’s a LongKeyedMapper just like the rest of the entities,
but it only contains foreign key fields to the other entities.
// Join Entity
class DepartmentIdTag extends LongKeyedMapper[DepartmentIdTag] with IdPK {
def getSingleton = DepartmentIdTag
object departmentid extends MappedLongForeignKey(this,DepartmentId)
object Employee extends MappedLongForeignKey(this,Employee)
}
object DepartmentIdTag extends DepartmentIdTag with LongKeyedMetaMapper[DepartmentIdTag] {
def join (departmentid : DepartmentId, tx : Employee) =
this.create.departmentid(departmentid).Employee(tx).save
}
To use the join entity, you’ll need to create a new instance and set the
appropriate foreign keys to point to the associated instances. As you can see,
we’ve defined a convenience method on our Expense meta object to do just that.
To make the many-to-many accessible as a field on our entities, we can use the
HasManyThrough trait, as shown below
// HasManyThrough for Many-to-Many Relationships
class Employee ... {
object departmentids extends HasManyThrough(this, DepartmentId,
DepartmentIdTag, DepartmentIdTag.departmentid, DepartmentIdTag.Employee)
}
I noticed that department is spelled in two ways:
departement
department
Perhaps the mapper does not know how to list * results from a join operation
Have you tried the following
SELECT
e.birthday as birthDay ,
e.departmentId as departmentId ,
e.salary as salary
FROM
Employee e
INNER JOIN Department d
ON e.departmentId = d.departmentId
WHERE
d.budget < 100 AND
e.salary > 10
Disclaimer: I have 0 experience with Mapper/Lift, but I do have experience with mapping query result sets to objects in Borland C++Builder, Delphi and Java. Some of these object-based systems are buggy and can't expand SELECT * to all the fields, so you need to EXPLICITLY tell them which fields to get.
In your case, you have e.departmentId and d.departmentId which can confuse Mapper into not knowing which one is the real departmentId in *. Some systems will actually return departmentId and departmentId_1 (this one gets the _1 tacked on to the end by default)
Other systems just hang, error out, have unpredictable behavior.
I've also seen the existence or non-existence of the termination ';' character at the end be an issue in some canned SQL apps.
You can always run any query you like to with exec or runQuery:
http://scala-tools.org/mvnsites/liftweb-2.4-M1/#net.liftweb.db.DB
Although you can do joins in Mapper as well.
You could use OneToMany or ManyToMany traits
To use ManyToMany you put your join fields. See example code:
class Meeting extends LongKeyedMapper[Meeting] with IdPK with CreatedUpdated with OneToMany[Long, Meeting] with ManyToMany {
def getSingleton = Meeting
object owner extends MappedLongForeignKey(this, User)
object title extends MappedString(this, 100)
object beginDate extends MappedDateTime(this)
object endDate extends MappedDateTime(this)
object location extends MappedString(this,100)
object description extends MappedText(this)
object allDay extends MappedBoolean(this)
object users extends MappedManyToMany(MeetingUser, MeetingUser.meeting, MeetingUser.user, User)
object contacts extends MappedManyToMany(MeetingContact, MeetingContact.meeting, MeetingContact.contact, Contact)
}
And Here is the joining entity.
class MeetingContact extends LongKeyedMapper[MeetingContact] with IdPK with CreatedUpdated {
def getSingleton = MeetingContact
object meeting extends MappedLongForeignKey(this, Meeting)
object contact extends MappedLongForeignKey(this, Contact)
}
object MeetingContact extends MeetingContact with LongKeyedMetaMapper[MeetingContact] {
def join(m: Meeting, c: Contact) = this.create.meeting(m).contact(c).save
def assignedTo(c: Contact) = this.findAll(By(MeetingContact.contact, c)).filter(_.meeting.obj.isDefined).map(_.meeting.obj.open_!)
override def beforeCreate() = MailSender.sendInviteToMeetingContact _ :: super.beforeCreate
}
Well, I know nothing about Liftweb mapper, but as far as SQL goes it would look something like:
select e.birthday, e.department_id, e.salary from
employee e left join department d on d.department_id=e.department_id
where d.budget>100 and e.salary>10;
The SQL is as follows.
SELECT *
FROM Employee e
INNER JOIN Department d
ON e.departmentId = d.departmentId
WHERE d.budget < 100 AND e.salary > 10

How to query Oracle database with NHibernate?

Help me translate this into proper NHibernate...
I have an Oracle database with 2 tables:
Employees:
Id (unique id)
FirstName (string)
LastName (string)
Location (string)
Locations:
Name (string)
Address (string)
As you can see the Employees table has a unique id, but the Locations table has no id whatsoever. The Name field is a regular field and is not unique.
In Oracle SQL I can run the following query:
SELECT *
FROM Employees e
LEFT OUTER JOIN Locations l
ON e.Location = l.Name
WHERE e.Id = 42
The location where the employee 42 is, has 2 rows in the Locations table, so this query returns 2 rows, one for each location found for employee 42.
Well, I can't figure out how to translate this into an NHibernate mapping + query (I use Fluent NHibernate). I tend to think that I should map Employees.Location as a Reference to Locations.Name, and so when running my HQL query it should return 2 objects, but NHibernate wouldn't let me retrieve a list from a Reference. So I tried HasMany but it doesn't work either, because NHibernate wants a field in Locations referring back to Employees, which kinda makes sense.
My HQL query looks like this:
select e
from Employees e
left join e.Locations l
where e.SGId like :sgId
Why can I do this in regular SQL and not in NHibernate?
Thanks.
I found the solution, you have to use HasMany(x => x.Locations) in conjunction with .PropertyRef("Location") so that hibernate knows that the field in Employee to be used for the join should be Location (and not the id of employee as is the default).
class Employee
{
public virtual int Id {get;set}
public virtual string FirstName {get;set;}
public virtual string LastName {get;set;}
public virtual string Location {get;set;}
public virtual IList<Location> Locations {get;set;}
}
class EmployeeMapping : ClassMap<Employee>
{
public EmployeeMapping()
{
Id(x=>x.Id);
Map(x=>x.FirstName);
Map(x=>x.LastName);
Map(x=>x.Location);
HasMany<Location>(x => x.Locations)
.PropertyRef("Location")
.KeyColumn("Name");
}
}
Also I found a few flaws in my approach due mainly to the weird legacy database I'm working with. That doesn't change the solution but I want to add that if one of the tables you're dealing with doesn't have unique ids, you're in trouble. Hibernate needs a unique id, so in my case I had to come up with a composite id made from multiple fields in order to make it unique. This is another debate but I wanted to mention it here to help whoever is researching this topic in the future.
For you mapping you need to use a collection/array/list/set object and a HasMany on your employees mapping for the location if it is going to return 2 locations for the one employee
class Employee
{
public virtual int Id {get;set}
public virtual string FirstName {get;set;}
public virtual string LastName {get;set;}
public virtual IList<Location> Location {get;set;}
}
class EmployeeMapping : ClassMap<Employee>
{
public EmployeeMapping()
{
Id(x=>x.Id);
Map(x=>x.FirstName);
Map(x=>x.LastName);
HasMany<Location>(x => x.Location)
.KeyColumn("Name")
.PropertyRef("Location")
.LazyLoad();
}
}