Jpa多对多如何仅将数据插入2个表

Posted

技术标签:

【中文标题】Jpa多对多如何仅将数据插入2个表【英文标题】:Jpa many-to-many how can I insert data into 2 tables only 【发布时间】:2014-10-10 08:50:29 【问题描述】:

我有一些问题。

我有 3 张桌子:

users (id, name, ..)
roles (id, name)
user-role (user_id, role_id)

当我执行多对多关系并执行 save() 时,我有 3 个插入。

用户:

@Entity
@Table(name = "user")
public class User implements Serializable 

    public static final String UK_EMAIL = "uk_email";

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST, CascadeType.MERGE)
    @JoinTable(
        name = "system_user_role",
        joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = id")
    )
    private List<SystemRole> userRole;

    public List<SystemRole> getUserRole() 
        return userRole;
    

系统角色;

@Entity
@Table(name = "system_role")
public class SystemRole implements Serializable 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column
    private String name;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "system_user_role",
        joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id")
    )
    private List<User> users;

    public List<User> getUsers() 
        return users;
    

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

    public Long getId() 
        return id;
    

    public void setId(Long id) 
        this.id = id;
    

    public String getName() 
        return name;
    

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

请告诉我,我可以只将数据插入 2 个表中,只插入(用户和用户角色? 我有角色列表,创建新用户时不需要添加新角色。

所以,当我这样做时:

SystemRole role1 = systemRoleService.findOne("ROLE_ADMIN");
userForm.setUserRole(Lists.newArrayList(role1));
....
final User saved = userRepository.save(user);
....

我收到一个错误:

org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to  persist:...

如果我这样做:

@Service("userService")
@Transactional
public class UserServiceImpl implements UserDetailsService, ResourceService<User> 
private final static Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);

@Autowired
private UserDAO userRepository;
@Autowired
private SystemRoleDAO systemRoleRepository;

@Override
@Transactional(rollbackFor = ResourceException.class)
public User create(User user) throws ResourceException 
    try 
        SystemRole role1 = systemRoleRepository.findOne(6l);
        user.setUserRole(Lists.newArrayList(role1));

        user.setId(62l); // !!! if set user ID - it works CORRECT

        final User saved = userRepository.save(user);
        return saved;
     catch (DataIntegrityViolationException ex) 
...

用户DAO:

@Repository
public interface UserDAO extends JpaRepository<User, Long> 
...

SystemRoleDAO:

@Repository
public interface SystemRoleDAO extends JpaRepository<SystemRole, Long> 

它有效,但我有 3 个插入。

当我创建一个新用户时,我需要从列表中选择一个角色,将其添加到用户并保存新用户。

非常感谢。

【问题讨论】:

你如何分配角色?看起来您正在创建新角色而不是使用现有角色。请避免使用像List 这样的原始类型。请改用List&lt;SystemRole&gt; 我猜有一个插入,因为角色,因为您创建一个新角色而不是分配一个现有角色,但您需要发布创建和保存的代码。 @user1647166 我编辑了你的问题,代码格式有问题。你能检查一下这个问题:***.com/questions/17887055/… 吗?看起来相关。 @Tom,谢谢,但我之前看过这篇文章,我有不同的情况。我需要使用存在的角色(我将从列表中选择它们),而不是每次创建新用户时都创建新角色。 看起来systemRoleService.findOne 在你的roleobject 上调用detach。如果是这样,请不要这样做。 detach 用于高级跨会话操作,而不是常规对象操作。 【参考方案1】:

@实体 @Table(name = "用户")

公共类用户实现可序列化

public static final String UK_EMAIL = "uk_email";

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST, CascadeType.MERGE)  //Make CascadeType.Detached this should solver your problem
@JoinTable(
    name = "system_user_role",
    joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
    inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = id")
)
private List<SystemRole> userRole;

public List<SystemRole> getUserRole() 
    return userRole;

【讨论】:

【参考方案2】:

我猜你的 userRepository.save() 方法中的代码正在调用

entityManager.persist(user);

你应该打电话

entityManager.merge(user);

通过这样做,Hibernate 不会插入新的角色,而是检查具有相同 ID 的角色是否已经存在,如果是这种情况,该角色将附加到用户实体(前提是级联类型包括合并操作)。

您需要调用合并的原因是您正在使用已加载的角色实体(通过 systemRoleService.findOne("ROLE_ADMIN"))然后从持久性上下文中分离,因此当您尝试时该实体被分离保存用户。如果没有分离 Role,您可以在 User 实体上调用 persist() 而不是 merge()。

【讨论】:

嗨,谢谢,但我使用org.springframework.data.jpa.repository.JpaRepository 接口并从中调用方法save() 看看这个***.com/questions/15943783/… 我已经更改了我的代码,但是... :( 请看一下更改。谢谢。 如果我打电话给user.setId() - 它工作正常,但我这样做只是为了测试。保存前我不知道用户 ID... 是的,因为在这种情况下,存储库将调用 merge() 而不是 persist()。您必须了解才能弄清楚您的交易中发生了什么。尝试在 TRACE 级别记录 org.springframework.transaction

以上是关于Jpa多对多如何仅将数据插入2个表的主要内容,如果未能解决你的问题,请参考以下文章

Spring数据JPA-休眠多对多关系在链接实体表中插入null

春季 JPA |在多对多关系中搜索

Mysql连表之多对多

jpa 多对多,带有附加列和复合键

实体框架:多对多插入重复

ORM表多对多的操作