【问题标题】:JpaRepository save() creating new child objects instead of updatingJpaRepository save() 创建新的子对象而不是更新
【发布时间】:2018-05-26 12:49:47
【问题描述】:

我目前正在使用 GSON 将以下 JSON 转换为两个 java 对象。 Company 对象包含一个一对一关系的 Accounts 对象。

JSON:

{
  "company_name": "ETHEREUM LTD",
  "accounts": {
    "next_made_up_to": "2018-11-30",
    "next_due": "2019-08-31"
  },
  "company_number": "11090535"
}

公司对象:

@Data
@Entity
@Table(name = "companies")
public class Company {

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

    @OneToOne(cascade=CascadeType.ALL)
    private Accounts accounts;

    @NotNull
    @SerializedName("company_number")
    @Column(unique = true)
    private String companyNumber;

    @SerializedName("company_name")
    private String companyName;

    public Company() {}

    public Company(String companyNumber) {
        this.companyNumber = companyNumber;
    }
}

账户对象:

@Data
@Entity
@Table(name = "accounts")
public class Accounts {

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

    @SerializedName("next_due")
    private Date nextDue;

    @SerializedName("next_made_up_to")
    private Date nextMadeUpTo;
}

我已经实现了一个 Spring Data JpaRepository 来将这些对象保存到我的数据库中。

public interface CompanyRepository extends JpaRepository<Company, Long> {
}

我有一种方法可以用新的 JSON 信息更新数据库中的所有 Company 对象。

@Scheduled(fixedRate = 50000)
public void updateAllCompanies() {
    System.out.println("Starting update!");
    for(Company company : companyRepository.findAll()) {
        Long companyId = company.getId();
        Long accountsId = null;
        if(company.getAccounts() != null) {
            accountsId = company.getAccounts().getId();
        }
        company = getCompany(company.getCompanyNumber());
        company.setId(companyId);
        if(accountsId != null) {
            company.getAccounts().setId(accountsId);
        }
        companyRepository.save(company);
    }
}

除非我手动设置父对象和子对象的 id,否则这些对象将创建为新的数据库条目,而不是更新现有条目。有没有比在 Java 中手动设置 id 更好的方法来处理这个问题?

【问题讨论】:

    标签: java hibernate jpa spring-data gson


    【解决方案1】:

    除非我手动设置父对象和子对象的 id,否则这些对象将创建为新的数据库条目,而不是更新现有条目。

    这就是每个持久性提供者的工作方式。

    如果实体的@Id 字段未标记为由持久性提供程序或数据库自动生成,则空值的存在将触发提供程序抛出异常。但是,如果该字段被标记为由提供者或数据库自动生成,它将跳过异常并将实体视为必须插入的新实例。只有在标识符字段中存在值的情况下才会触发对现有行的更新。

    有没有比在 Java 中手动设置 id 更好的方法来处理这个问题?

    这取决于。

    如果 JSON 有效负载可以为您提供 id 值,您可以简单地将它们序列化到对象中,并且持久化过程就像您手动分配它们一样工作。这意味着具有标识符值的实体将被更新,没有标识符值的实体将被插入。

    如果这不是一个合理的选项,那么如果您打算使用 @Entity 代表您的 Accounts,则需要分配它们。

    我不确定您的 Accounts 实体是否被您的数据模型中的任何其他内容引用。但这里要考虑的一件事是,@ElementCollection 的可嵌入对象也可以非常轻松地解决您的标识符问题:

    @Embeddable 
    public class Accounts implements Serializable {
      private Date nextDue;
      private Date nextMadeUpTo;
      // implement proper equals/hashcode here
    }
    
    @Entity
    public class Customer {
      // your normal things
      @ElementCollection
      private Set<Accounts> Accounts;
    }
    

    现在您需要做的就是将您的 JSON 获得的任何数据添加到 Accounts 中,并将其放入元素集合中。然后,Hibernate 将使用如下的集合表来确定是否需要为您删除或插入某些内容:

    CUSTOMER_ID | NEXT_DUE | NEXT_MADE_UP_TO
    

    Accounts 可嵌入对象上正确使用equals / hashCode 可以保证没有重复,它还有助于确定是否修改了哪些内容。基础集合表现在使用可嵌入的所有字段 + Customer 主键作为表的主键。这通过将数据用作自然键来避免代理主键的问题。

    【讨论】:

    • 感谢您的建议,我不知道可嵌入的注释,因此必须阅读更多内容:)
    猜你喜欢
    • 1970-01-01
    • 2018-05-09
    • 1970-01-01
    • 2018-10-25
    • 2017-02-25
    • 2019-09-20
    • 2014-01-31
    • 2015-07-26
    • 2019-03-12
    相关资源
    最近更新 更多