2012-10-22 26 views
5

Obecnie pracuję nad aplikacją Grails i pracuję z wtyczkami Spring Security i UI. Stworzyłem relację pomiędzy klasą użytkownika i innej klasy Area, że ​​mam co widać poniżej:Grails - zapisz instancję przejściową przed wypróżnieniem Grails Error?

klasy użytkownika:

class User { 

    transient springSecurityService 

    String username 
    String email 
    String password 
    boolean enabled 
    boolean accountExpired 
    boolean accountLocked 
    boolean passwordExpired 

    static belongsTo = [area:Areas] 

     ....... 
} 

palących Klasa:

class Areas { 

    String name 

    static hasMany = [users:User] 

} 

Jak widać z klas jeden użytkownik może być połączony z jednym obszarem, ale obszar może mieć wielu użytkowników. To wszystko działa dobrze i kiedy ładuję aplikację, wszystkie dane są dodawane poprawnie. Jednak pojawia się następujący błąd, gdy próbuję skorzystać z formularza w celu utworzenia nowego użytkownika:

object references an unsaved transient instance - save the transient instance before flushing: com.website.Area 

poniżej jest kod kontroler używam, aby zapisać informacje:

def save = {  
     def user = lookupUserClass().newInstance(params) 

     if (params.password) { 
      String salt = saltSource instanceof NullSaltSource ? null : params.username 
      user.password = springSecurityUiService.encodePassword(params.password, salt) 
     } 

     if (!user.save(flush: true)) { 
      render view: 'create', model: [user: user, authorityList: sortedRoles()] 
      return 
     } 

     addRoles(user) 
     flash.message = "${message(code: 'default.created.message', args: [message(code: 'user.label', default: 'User'), user.id])}" 
     redirect action: edit, id: user.id 
    } 

i tu jest próbkę tego, co GSP wygląda następująco:

<table> 
     <tbody> 

      <s2ui:textFieldRow name='username' labelCode='user.username.label' bean="${user}" 
          labelCodeDefault='Username' value="${user?.username}"/> 

      <s2ui:passwordFieldRow name='password' labelCode='user.password.label' bean="${user}" 
           labelCodeDefault='Password' value="${user?.password}"/> 

      <s2ui:textFieldRow name='email' labelCode='user.email.label' bean="${user}" 
           labelCodeDefault='E-Mail' value="${user?.email}"/> 

      <s2ui:textFieldRow readonly='yes' name='area.name' labelCode='user.area.label' bean="${user}" 
           labelCodeDefault='Department' value="${area.name}"/> 


      <s2ui:checkboxRow name='enabled' labelCode='user.enabled.label' bean="${user}" 
          labelCodeDefault='Enabled' value="${user?.enabled}"/> 

      <s2ui:checkboxRow name='accountExpired' labelCode='user.accountExpired.label' bean="${user}" 
          labelCodeDefault='Account Expired' value="${user?.accountExpired}"/> 

      <s2ui:checkboxRow name='accountLocked' labelCode='user.accountLocked.label' bean="${user}" 
          labelCodeDefault='Account Locked' value="${user?.accountLocked}"/> 

      <s2ui:checkboxRow name='passwordExpired' labelCode='user.passwordExpired.label' bean="${user}" 
          labelCodeDefault='Password Expired' value="${user?.passwordExpired}"/> 
     </tbody> 
     </table> 

Patrzyłem na to i uważam, że jest to sposób próbuję zapisać obiekt Gorm i mam może zapisać rodzica (obszar) przed próbą zapisania użytkownik. Jednak Obszar zawsze będzie istniał, zanim użytkownik będzie mógł zostać utworzony, więc nie potrzebuję ponownego tworzenia obszaru, jak sobie z tym poradzić? Wypróbowałem funkcję "withTransaction" z niewielkim sukcesem.

Naprawdę doceniam pomoc, jeśli to możliwe.

Dzięki

EDIT ......

mam śledzone problemu do tej linii w kontrolerze:

RegistrationCode registrationCode = springSecurityUiService.register(user, command.password, salt) 

to mówi mi, że ta funkcja jest wywołanie strzał dla obiektu użytkownika, jednak w przypadku istniejących relacji musi najpierw zapisać obszar. Czy ktoś zna SpringSecurityUi, aby mi w tym pomóc?

Dzięki

+0

Umieść usługę springsecurity w kontrolerze, a nie w klasie domeny. – marko

+0

Czy możesz podać mi przykład, proszę? ponieważ usługa springsecurity zawsze tam była – user723858

+0

Czy błąd nie byłby powiązany z klasą "com.website.Area", jak mówi błąd? Dzięki – user723858

Odpowiedz

7

Komunikat o błędzie widzisz

object references an unsaved transient instance - save the transient instance before flushing: com.website.Area 

... dzieje się, gdy próbujesz zapisać nieistniejącego rodzica (np. Obszar) z jednostki podrzędnej (tj. użytkownika). Możliwe, że błąd występuje w metodzie RegistrationController.register, jak zauważyłeś, jeśli to miejsce, w którym zapisujesz nowego użytkownika.

RegistrationCode registrationCode = springSecurityUiService.register(user, command.password, salt) 

Wystarczy zaktualizować kod rejestracyjny .Zarejestruj metodę z logiką, aby przypisać obszar Użytkownikowi (zakładając, że Obszar już istnieje) przed rejestracją rejestracja połączenie - poniżej jest szybki przykład.

class RegistrationController .. 
.. 
def area = Area.findByName(command.areaName) 
def user = lookupUserClass().newInstance(email: command.email, username: command.username, 
       accountLocked: true, enabled: true, area: area) 
RegistrationCode registrationCode = springSecurityUiService.register(user, command.password, salt) 

Kilka uwag:

  • są przechodzącą powrotem „area.name” z GSP widzenia, więc trzeba zaktualizować/nadpisać RegisterCommand włączenie palących nazwę
  • Czy używasz Area "name" jako identyfikatora bezpiecznego? W definicji klasy nie ma ograniczeń wskazujących, że "nazwa" będzie niepowtarzalna. Może przechodząc z powrotem się palących ID z widoku jest bezpieczniejszy
  • idealnie powinien obsługiwać palących odnośnika z niestandardowych edytora własności - oto przykład: Grails command object data binding

Anyways, nadzieja, że ​​pomaga.

+0

Life Saver :-) Rozwiązałem już problem w jednej części rozwiązania, ale to naprawdę pomogło mi rozwiązać następną :-D – user723858

+0

Najlepsza odpowiedź ... i dobre wyjaśnienie pytania –

1

spróbuj włączyć dwukierunkową zależność na drugą stronę

class Areas { 

    String name 
    static belongsTo = [User] 
    static hasMany = [users:User] 
} 

Nie zapomnij usunąć belongsTo z Użytkownikiem

1
class Areas { 
    String name 
    static belongsTo = [User] 
    static hasMany = [users:User] 
}