Grails TransientObjectException

时间:2014-01-31 19:24:07

标签: spring grails ldap

这与this post有关,您可以在其中找到我的大部分配置。 ldap用户映射到数据库用户表,并且条目创建正常。然后userDetails尝试从主用户类获取权限,从而导致以下异常:

2014-01-31 12:10:52,076 [http-bio-8111-exec-4] ERROR [/step].[default]  - Servlet.service() for servlet [default] in context with path [/step] threw exception
Message: object references an unsaved transient instance - save the transient instance before flushing: packagae.User; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: package.User
    Line | Method
->>  102 | doCall                in org.grails.datastore.gorm.GormStaticApi$_methodMissing_closure2
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|     42 | getAuthorities        in package.User
|     27 | getAuthorities . . .  in package.MdtUserDetails
|     72 | attemptAuthentication in grails.plugin.springsecurity.web.authentication.RequestHolderAuthenticationFilter
|     49 | doFilter . . . . . .  in     ''
|     82 | doFilter              in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
|   1145 | runWorker . . . . . . in java.util.concurrent.ThreadPoolExecutor
|    615 | run                   in java.util.concurrent.ThreadPoolExecutor$Worker
^    744 | run . . . . . . . . . in java.lang.Thread
Caused by TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: package.User
->>  102 | doCall                in org.grails.datastore.gorm.GormStaticApi$_methodMissing_closure2
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|     42 | getAuthorities        in package.User
|     27 | getAuthorities . . .  in package.MdtUserDetails
|     72 | attemptAuthentication in grails.plugin.springsecurity.web.authentication.RequestHolderAuthenticationFilter
|     49 | doFilter . . . . . .  in     ''
|     82 | doFilter              in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
|   1145 | runWorker . . . . . . in java.util.concurrent.ThreadPoolExecutor
|    615 | run                   in java.util.concurrent.ThreadPoolExecutor$Worker
^    744 | run . . . . . . . . . in java.lang.Thread

以下是我的用户详细信息类:

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority
import org.springframework.security.ldap.userdetails.LdapUserDetails

import package.Role
import package.User

class MdtUserDetails extends User implements LdapUserDetails{



    public MdtUserDetails(String fullName, String email, String username, String password, boolean enabled, boolean accountExpired,
        boolean accountLocked, boolean passwordExpired, Collection<GrantedAuthority> authorities) {
        super(username: username, password: password, email: email, fullName: fullName, enabled: enabled, accountExpired: accountExpired, accountLocked: accountLocked, passwordExpired: passwordExpired, authorties: authorities)

    }


    @Override 
    public Set<Role> getAuthorities(){
        return super.getAuthorities()
    }

    @Override
    public boolean isAccountNonExpired() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public String getDn() {
        // TODO Auto-generated method stub
        return null;
    }


}

用户:

class User {

    transient springSecurityService

    String username
    String toString() {
        "${username}"
    }
    String password
    String email
    String fullName
    String userOrg
    boolean enabled = true
    boolean accountExpired
    boolean accountLocked
    boolean passwordExpired

    static belongsTo = [organization : Organization]
    static hasMany = [reports: Report, invoices: Invoice]

    static mappedBy = [invoices:'lastUpdatedBy', reports: 'lastUpdatedBy']
    static transients = ['springSecurityService']

    static constraints = {
        username blank: false, unique: true
        //have to nullable true the password in order to map mdtUsers to applicaiton roles.
        password nullable: true, blank: true
        email blank: true, nullable: true
        fullName nullable: true, blank: true
        userOrg nullable: true, blank: true
        organization nullable: true, blank: true
    }

    static mapping = {
        table 'step_users'
        password column: '`password`'
    }

    Set<Role> getAuthorities() {
        UserRole.findAllByUser(this).collect { it.role } as Set
    }

    def beforeInsert() {
        encodePassword()
    }

    def beforeUpdate() {
        if (isDirty('password')) {
            encodePassword()
        }
    }

    protected void encodePassword() {
        password = springSecurityService.encodePassword(password)
    }
}

我不确定这是否与级联事件有关,或者我应该如何保存会话,或者我如何在UserDetails中调用超类?

编辑

终于搞清楚了!

在UserDetails类中,我正在扩展自己的用户类:

import package.User

class MdtUserDetails extends User {

相反,我需要从这里扩展springsecurity用户类:

import org.springframework.security.core.userdetails.User

我认为这导致了我的Transient对象异常,因为答案提示你需要拥有belongsTo等等。我确实在我的课程中有那些。

1 个答案:

答案 0 :(得分:1)

Caused by TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: package.User

此消息告诉您要保存的User对象包含至少一个未保存的另一个域对象的引用。

根据您的User类,此未保存的引用可以是ReportInvoice对象。 您有以下选项可以解决此问题:

在将报告和发票添加到用户之前手动保存报告和发票

使用

启用报表和发票的自动级联
static mapping = {
  reports cascade: 'all-delete-orphan'
  reports invoices: 'all-delete-orphan'
}

belongsToInvoice添加Report关系:

static belongsTo = [user: User]