1

Ok, I am lost... I have a domain class Student and Course. The class Course is set. I do not add any new Courses into the table. A student can have many courses and a course can have many students. Please take a look at what I have and direct me to the right way.

class Student {
    String fullName

    static belongsTo = [schools: School]
    static hasMany = [courses:Course]

    static mapping = {
        table "STUDENT"
        courses joinTable: "STUDENT_COURSE", key: "COURSE_ID"  
}

class Course {
    String courseName   

    static hasMany = [students:Student]
    static belongsTo = Student

    static mapping = {
        students joinTable: "STUDENT_COURSE", key: "STUDENT_ID"
}

Now, when I enter new student information, the view doesn't blow up, so that's good... But it saves student info but the joinTable STUDENT_COURSE is blank. I know somehow I need to pass the PK ID of studnet and course to the joinTable, but I don't know how and where. (Does it go in the studentController under def save()?) Also, what should .gsp look like? This is what I have and I know I have it wrong. (id="coursetags" is jQuery autocomplete id)

My _form.gsp input part.

<div class="fieldcontain ${hasErrors(bean: studentInstance, field: 'courses', 'error')} required" >
<label for="course">
    <g:message code="student.courses.label" default="Course" />
    <span class="required-indicator">*</span>
</label>
<g:textField name="course" id="coursetags" required="" value="${course?.course}"/>

My result should look like this.

STUDENT             COURSE                      STUDENT_COURSE
ID      FULLNAME    ID          COURSENAME      STUDENT_ID   COURSE_ID
1       John Doe    1           English101        1             1
2       Jane Smith  2           Science101        1             2
                                                  2             2

I've been trying to analyze these sites...

http://chrisbroadfoot.id.au/2008/07/19/many-to-many-relationship-mapping-with-gorm-grails/

https://grails.github.io/grails-doc/3.0.x/guide/GORM.html#manyToMany

https://grails.org/wiki/Many-to-Many%20Mapping%20without%20Hibernate%20XML

Thank you.

EDIT 1

My controller

class studentController {
    @Transactional
    def save(Student studentInstance) {
        if (studentInstance == null) {
            notFound()
            return
        }

        if (studentInstance.hasErrors()) {
            respond studentInstance.errors, view:'create'
            return
        }  

    def courseID = Course.findByCourseLike(params.course)

    studnetInstance.save flush:true

    request.withFormat {
        form multipartForm {
            flash.message = message(code: 'default.created.message', args: [message(code: 'student.label', default: 'Student'), studnetInstance.id])
            redirect studentInstance
        }
        '*' { respond studentInstance, [status: CREATED] }
    }

    def sID = studentInstance.id    //When I println under these, they print the right id numbers.
    def cID = courseID.id

    student.addToCourses(cID).save()  //This is the part that I don't understand.
                                     //I get error saying No such property: student for class.

    }
}

EDIT 2 So, I was thinking and doing some research.... Unless I use SQL directly to STUDENT_COURSE join table, I need to create a separate domain class for StudentCourse and map both Student and Course classes to StudentCourse. Is that correct?

monty_bean
  • 494
  • 5
  • 25

2 Answers2

0

Generally speaking, when dealing with a many-to-many association you need to

  1. call parent.addTo*(child) to add instances to the collection, and then call parent.save() to persist them.
  2. use key instead of column in both joinTable mappings.

In addition, I recommend using the org.springframework.transaction.annotation.Transactional annotation on controller methods which call save() so that you won't need to flush explicitly. In your case, Student is the owner of the association (because Course is set to belongTo Student).

import org.springframework.transaction.annotation.Transactional

class StudentController {

    @Transactional
    def save() {
        def student = /* some Student instance */
        def course = /* some Course instance */

        student.addToCourses(course)
        student.save()
    }
}

Essentially, the addTo*() method takes care of the join table.

GSP

So that takes care of persistence. As for the GSP... well on StackOverflow asking multiple questions at once is frowned upon. But here's the general idea with very minimal HTML/GSP:

<g:each var="student" in="${students}">
  <g:each var="course" in="${student.courses}">
    <p>${student.id}  ${student.fullName}  ${course.id}  ${course.courseName}</p>
  </g:each>
</g:each>

For more on the GSP, please create a new question.

Emmanuel Rosa
  • 9,697
  • 2
  • 14
  • 20
0

Your joinTable mapping needs correction. joinTable either accepts a String or a Map with keys name, key, and column.

Change

courses joinTable: "STUDENT_COURSE", key: "COURSE_ID"

and

students joinTable: "STUDENT_COURSE", key: "STUDENT_ID"

To

courses joinTable: [name: "STUDENT_COURSE", key: "COURSE_ID"]

and

students joinTable: [name: "STUDENT_COURSE", key: "STUDENT_ID"]
Maicon Mauricio
  • 2,052
  • 1
  • 13
  • 29