如果数据库中存在,则重用现有 ID

Reuse an existing id if exist in the database

我想做以下事情:

正在使用 JPA 将 CityHistory 插入数据库。 第一次没有数据,所以会插入一个新的城市。 (它工作正常) 城市 table 中的 (IDENTIFICATION) 是一个唯一字段。

我想要实现的是,当我再次插入同一个城市时,重用现有字段而不是尝试创建一个新字段(标识就像一个城市的唯一名称)。

那么我如何使用 JPA 或 Hibernate 来做到这一点?

@Entity
public class CityHistory extends History implements Serializable {

    @Id
    @Column(name = "KEY_CITY_HISTORY", nullable = false, precision = 19)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "CITY_ID", nullable = false, foreignKey = @ForeignKey(name = "FK_CITY_ID"))
    private City cityId;

    @Column(name = "CITY_NAME", nullable = false)
    private String cityName;
}


@Entity
public class City implements Serializable {

    @Id
    @Column(name = "KEY_CITY", nullable = false, precision = 19)
    private Long id;

    @Column(name = "IDENTIFICATION", nullable = false, unique = true)
    private String identification;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "MUNICIPALITY_ID", foreignKey = @ForeignKey(name = "FK_MUNICIPALITY_ID"))
    private Municipality municipalityId;
}

更新 这是我将数据写入数据库的方式, 这是一个Spring批处理itemWriter

@Component
public class InfoItemWriter implements ItemWriter<Object> {

    @Autowired
    private CityHistoryRepository cityHistoryRepository;

    @Override
    public void write(List<? extends Object> items) throws Exception {

        if (items.size() > 0 && items.get(0) instanceof CityHistory) {
            cityHistoryRepository.saveAll((List<? extends CityHistory>) items);
        }
    }
}

Hibernate 会使用 City 的 @Id 属性 来判断是否是新的。当它是 null 时,Hibernate 不可能知道类似的条目已经存在。 所以你需要执行查询先找到每个城市:

    for (var history : histories) {
        var cities = em.createQuery("select city from City city where city.identification = ?1", City.class)
                        .setParameter(1, history.getCityId().getIdentification())
                        .getResultList();

        if (!cities.isEmpty()) {
            history.setCityId(cities.get(0));
        }

        em.persist(history);
    }

如果你使用 Hibernate 并且 City.identification 是唯一的并且总是非空的,你可以将它用作 NaturalID:

所在城市:

@NaturalId
private String identification;

然后:

    for (var history : histories) {
        var city = em.unwrap(Session.class)
                .byNaturalId(City.class)
                .using("identification", history.getCityId().getIdentification())
                .getReference();

        if (city != null) {
            history.setCityId(city);
        }

        em.persist(history);
    }

但是如果你确实设置了City.id,即notnull,你可以使用EntityManager.merge获取托管实体:

    for (var history : histories) {
        City city = history.getCityId();
        if (city.getId() != null) {
            city = em.merge(city);
            history.setCityId(city);
        }

        em.persist(history);
    }

再说一句:我们不是在关系领域,而是在映射对象图。因此,将您的字段称为 cityId 和 municipalityId 可以说是 错误的 - 即使类型也是这样说的:City cityId。 它们不仅仅是简单的标识符,而且是完整的对象:City city.

首先感谢所有试图提供帮助的人!

正在阅读 @Benjamin Maurer 提供的资源:

I don't think you want the cascade on the ManyToOne side, see One-To-Many

最常见的父子关联由一对多和多对一关系组成,其中级联仅对一对多方有用

由于我的关系是 ManyToOne,所以使用级联确实没有用,不能满足我的需要。

我使用了不同的方法来达到目标​​。我创建了一个服务,它验证一个城市是否存在,如果不存在则添加一个新城市。

@Service
public class CityHistoryServiceImpl implements CityHistoryService {
    @Autowired
    CityRepository cityRepository;
    @Autowired
    CityHistoryRepository cityHistoryRepository;

    @Override
    public Optional<CityHistory> addCityHistory(City city, String cityName, ..) {

        if (city != null && cityName != null) {

            City city1 = addCityIfNotExist(city);

            CityHistory cityHistory = new CityHistory();
            cityHistory.setCityId(city1);
            cityHistory.setCityName(cityName);

            cityHistoryRepository.save(cityHistory);
            return Optional.of(cityHistory);
        }
        return Optional.empty();
    } ....

    private City addCityIfNotExist(City city) {
        City city1 = cityRepository.findFirstByBagId(city.getBagId());
        if (city1 == null) {
            city1 = cityRepository.save(city);
        }
        return city1;
    }
}