在保存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; }