JpaRepository save()创建新的子对象而不是更新

时间:2018-05-26 12:49:47

标签: java hibernate jpa spring-data gson

我目前正在使用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之外,还有更好的方法吗?

1 个答案:

答案 0 :(得分:2)

  

除非我在父对象和子对象上手动设置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

正确使用equals / hashCode Accounts嵌入保证不会重复,它也有助于确定修改或不修改的内容。底层集合表现在使用可嵌入+ Customer主键的所有字段作为表的主键。通过将数据用作自然键,可以避免代理主键的问题。