ManyToMany【项目随笔】关于异常object references an unsaved transient instance

在保存ManyToMany  时出现异常: 

org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: object references an unsaved transient instance

如图:

出现原因:单向ManyToMany保存顺序反了,应当先保存主控端permission对象

错误代码:

@Transactional(readOnly=false)
    @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
    @CacheEvict(value = "permissionList", allEntries = true)
    public Permission save(Permission permission) {
        permission.setMenu(menuDao.findOne(permission.getMenuId()));
        
        List<Role> roles = roleDao.findByDataScope(DataScope.ALL.getValue());
        for(Role temp : roles){
            List <Permission> permissions = temp.getPermissions();
            permissions.add(permission);
        }
        roleDao.save(roles);
        permission.setRoles(roles);  //权限归属于该角色
        return permissionDao.save(permission);
    }

两个类,角色Role和权限Permission,一个角色可以拥有多个权限,反之亦然。  

代码如下:

package net.myspring.blue.modules.sys.entity;

import java.util.List;

import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.google.common.collect.Lists;

import net.myspring.blue.common.persistence.DataEntity;
import net.myspring.blue.common.utils.Collections3;


/**
 * The persistent class for the sys_role database table.
 * 
 */
@Entity
@Table(name="sys_role")
@Cacheable
public class Role extends DataEntity {
    private String code;
    private Integer dataScope;
    private String name;
    private List<Permission> permissions=Lists.newArrayList();
    private List<User> users=Lists.newArrayList();
    
    private List<Long> permissionIds=Lists.newArrayList();

    public Role() {
    }

    public String getCode() {
        return this.code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    @Column(name="data_scope")
    public Integer getDataScope() {
        return this.dataScope;
    }

    public void setDataScope(Integer dataScope) {
        this.dataScope = dataScope;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //bi-directional many-to-many association to Permission
    @ManyToMany
    @JoinTable(name = "sys_role_permission", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = { @JoinColumn(name = "permission_id") })
    public List<Permission> getPermissions() {
        return this.permissions;
    }

    public void setPermissions(List<Permission> permissions) {
        this.permissions = permissions;
    }


    //bi-directional many-to-many association to User
    @ManyToMany(mappedBy="roles")
    public List<User> getUsers() {
        return this.users;
    }

    public void setUsers(List<User> users) {
        this.users = users;
    }

    @SuppressWarnings("unchecked")
    @Transient
    public List<Long> getPermissionIds() {
        if(permissionIds!=null && permissionIds.size()==0 && Collections3.isNotEmpty(permissions)) {
            permissionIds= Collections3.extractToList(permissions, "id");
        }
        return permissionIds;
    }

    public void setPermissionIds(List<Long> permissionIds) {
        this.permissionIds = permissionIds;
    }
}

权限Permission类

package net.myspring.blue.modules.sys.entity;

import javax.persistence.*;

import net.myspring.blue.common.config.Global;
import net.myspring.blue.common.persistence.DataEntity;
import net.myspring.blue.common.utils.Collections3;

import com.google.common.collect.Lists;

import java.util.List;


/**
 * The persistent class for the sys_permission database table.
 * 
 */
@Entity
@Table(name="sys_permission")
@Cacheable
public class Permission extends DataEntity {
    private String name;
    private String permission;
    private Menu menu;
    private List<Role> roles=Lists.newArrayList();
    
    private Long menuId;

    public Permission() {
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public String getPermission() {
        return this.permission;
    }

    public void setPermission(String permission) {
        this.permission = permission;
    }

    //bi-directional many-to-one association to Menu
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="menu_id")
    public Menu getMenu() {
        return this.menu;
    }

    public void setMenu(Menu menu) {
        this.menu = menu;
    }


    //bi-directional many-to-many association to Role
    @ManyToMany(mappedBy="permissions")
    public List<Role> getRoles() {
        return this.roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
    
    @Transient
    public Long getMenuId() {
        if(menuId==null && menu!=null) {
            menuId=menu.getId();
        }
        return menuId;
    }

    public void setMenuId(Long menuId) {
        this.menuId = menuId;
    }

    @Transient
    public String getRoleNames() {
        return Collections3.extractToString(roles, "name", Global.CHAR_COMMA);
    }
}

注意主控端是Permission类 ,注解“ @ManyToMany(mappedBy="permissions") ”说明了这点

业务场景:

  admin角色是拥有"任意权限"的,我在添加新权限的时候,权限应当同步被admin所拥有。

权限对应的的角色组成的List<Role>也应当等于拥有”任意权限"的角色 .->List<Role> roles = roleDao.findByDataScope(DataScope.ALL.getValue());

保存时,一定要先save主控端permission,否则数据库将报错

正确的写法

@Transactional(readOnly=false)
    @Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
    @CacheEvict(value = "permissionList", allEntries = true)
    public Permission save(Permission permission) {
        permission.setMenu(menuDao.findOne(permission.getMenuId()));
        List<Role> roles = roleDao.findByDataScope(DataScope.ALL.getValue());
        permission.setRoles(roles);
        permissionDao.save(permission);  //注意save的顺序  permission先
        for(Role temp : roles){
            List <Permission> permissions = temp.getPermissions();
            permissions.add(permission);
        }
        roleDao.save(roles);  //roles后
        return null;
    }
原文地址:https://www.cnblogs.com/ChenJunHacker/p/4586796.html