【问题标题】:Spring Data Update Many To One PropertySpring Data 更新多对一属性
【发布时间】:2017-06-30 17:46:26
【问题描述】:

我在 Spring Data JPA 中保存多对一字段时遇到问题。 考虑下面简要描述的实体用户和组:

    @Entity
    @Table(name = "Users")
    public class User {
        @Basic(optional = false)
        @Column(name = "username")
        private String username;

        @JoinColumn(name = "group_id", referencedColumnName = "id")
        @ManyToOne
        private Group group;
    }

    @Entity
    @Table(name = "groups")
    public class Group {
       @Basic(optional = false)
       @Column(name = "name")
       private String name;

       @OneToMany(mappedBy = "group",cascade=CascadeType.PERSIST)
       private Collection<User> userCollection;
    }

当我想更新用户实体并使用 CrudRepository 保存方法更改其组时,发生以下异常: org.hibernate.HibernateException: Group 实例的标识符已从 2 更改为 1 并且这表明 spring 数据想要编辑 Group 的 id 字段,而它不正确并且 我想更改参考它不是它的 id。

对于更新,我以 DTO 形式获取视图数据,然后使用 Dozer Mapper 将 dto 对象转换为实体对象,如下所述:

DozerBeanMapper mapper = new DozerBeanMapper();
// get user by id from database for editing
User user = this.userService.findByIdAndDeletedFalse(form.getId());
// merge view data and entity data using Dozer
mapper.map(form, user);
// save User entity
this.userService.save(user);

userService 是一个只调用 crudRepository 保存方法的 bean。

有什么办法吗?

谢谢

【问题讨论】:

  • 用户和组共享同一张表是否正确?
  • 请附上更新相关代码。
  • 亲爱的 al27091 这是一个错误
  • 您可以在更新User.group 的位置添加代码吗?错误消息听起来好像您正在为Group 实体设置一个新的id,而不是更改User 中的引用
  • @crizzis 我将 user.group.id 更改为新选择的组 id,然后调用 save 方法。如果组为空,这对我来说很好,但是如果分配了一个组并且我想更改它,我将面临该异常。

标签: java spring hibernate spring-mvc jpa


【解决方案1】:

这是因为您使用的是临时组。您需要获取您的组,然后将该组分配给您的用户对象。

【讨论】:

    【解决方案2】:

    我认为您应该使用以下 xml 告诉 Dozer 排除 group 字段。

    <field-exclude> 
      <a>group</a> 
    </field-exclude>   
    

    然后手动设置group。

    mapper.map(form, user);
    user.setGroup(this.groupService.finfById(form.group.getId()));
    // save User entity
    this.userService.save(user);
    

    如果您不想在控制器中映射组。您可以使用DozerConverter 类,您可以在其中获取group 对象并将其设置在那里。

    <mapping>
      <class-a>org.mypackage.Form</class-a>
      <class-b>org.mypackage.User</class-b>    
      <field custom-converter-id="org.mypackage.UserGroupCustomConverter">
        <a>groupId</a>
        <b>group</b>
      </field>
    </mapping>  
    
    public class UserGroupCustomConverter extends DozerConverter<Form, User> {
    
      public NewDozerConverter() {
        super(Form.class, User.class);
      }
    
      public Boolean convertTo(Form form, User user) {
         user.setGroup(this.groupService.finfById(form.group.getId()));
      }
    }
    

    【讨论】:

    • 谢谢@alayor 先生,我不想在控制器中获取组,我希望映射并传递视图数据以保存编辑用户组关系。根据这句话你有什么建议吗?
    【解决方案3】:

    发生错误是因为您试图更改被跟踪实体的id,这在 JPA 中是不允许的。您需要获取对新Group 的引用并将其分配给User.group。

    简而言之,而不是这个:

    user.getGroup().setId(newId);
    

    试试这个:

    user.setGroup(entityManager.getReference(Group.class, newId)); 
    

    【讨论】:

    • 谢谢亲爱的@crizizis 我知道该解决方案有效,但我不想在控制器中获取组,我希望映射并传递视图数据以保存编辑用户组关系的原因。根据这句话你有什么建议吗?
    • 嗯...如果你没有真正加载新组,你怎么知道有这样一个 id 的组(仍然)存在? JPA 应该如何知道?无论如何,它必须通过数据库进行验证。我认为没有任何解决方法。请注意,EntityManager.getReference 将不会加载Group 实体的所有字段;它只会加载id(这就是find和getReference之间的区别)
    • 另外,你不应该在控制器中加载Group。您应该在事务的上下文中将其加载到您的服务方法中
    • 在视图中我有一个下拉框,包括所有组对象并传递给控制器​​,我有组对象,它的 id 甚至其他字段。
    • 如果您对新组有参考,请致电user.setGroup(newGroup)
    猜你喜欢
    • 2018-10-13
    • 1970-01-01
    • 1970-01-01
    • 2018-06-09
    • 1970-01-01
    • 1970-01-01
    • 2012-11-27
    • 1970-01-01
    • 2018-05-26
    相关资源
    最近更新 更多