Complex SQL Query With Join In Liftweb
Asked Answered
D

5

13

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.

Douse answered 12/12, 2011 at 19:20 Comment(8)
Are they two databases or tables?Lobworm
sorry for my formulation, I am talking about two tables. However I found a solution by doing a many-to-many connection in Employees to Departements. Then I do my request for employees and filter the list for options I want from departements. However if you have a complete SQL solution I would appreciate it.Douse
The best place to learn about queries is [wiki]( assembla.com/spaces/liftweb/wiki/Mapper). I'm glad you have something workingLobworm
Can you provide an example of the type of result set you wish to see?Buckinghamshire
I would like to get a list of employesDouse
@WarrenRox Do you need me to be more specific?Douse
@ChrisJamesC Please provide more information. There are a lot of different ways to show two tables in a join. Specifically, what columns do you want in the results(especially the column[s] that are common in both tables)? An example of your desired result table would be ideal.Lisbethlisbon
@IsaacFife I added some more details in my original postDouse
E
4

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)  
}  
Enlargement answered 13/2, 2012 at 21:23 Comment(1)
Thanks a lot for your very complete answer, reading the source you provided I found this chapter which could also help: exploring.liftweb.net/master/index-8.html#sub:SQL-based-queries I will do some test and post an answer if it works to. However your answer is very interesting and will certainly help a lot of SO users.Douse
E
1

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.

Enlargement answered 13/2, 2012 at 20:54 Comment(1)
Thanks for the typo, I am french and department is spelled departement in french :) Thanks for the complete query.Douse
M
1

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
}
Ml answered 15/2, 2012 at 11:30 Comment(2)
Hi, can you please extend your answer?Douse
Sorry it took so long, extended :)Ml
L
-1

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;
Lisbethlisbon answered 27/1, 2012 at 20:45 Comment(3)
My goal is to be able to perform some complex join SQL queries using lift structures. The SQL part is not a problem and I tried to build the simplest example, maybe too simple. However thanks very much for your help!Douse
@ChrisJamesC: Is the problem, that the join is dependent from the data? Do you need different joins depending on the data?Laureate
@Laureate Honestly I don't know, when I do my request on phpmyadmin it works, and when I want to do it in Scala the Join doesn't work.Douse
D
-1

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
Decisive answered 8/2, 2012 at 12:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.