Home > Blockchain >  Reuse an existing id if exist in the database
Reuse an existing id if exist in the database

Time:09-17

I would like to do the following:

Inserting the CityHistory into the database using JPA. The first time there is no data, so a new city will be inserted. (IT WORKS FINE) the (IDENTIFICATION) within the city table is a unique field.

What I want to achieve is when I am inserting the same city again is to reuse the existing field instead of trying to create a new one (identification will be like a city's unique name).

So how can I do that using JPA or 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;
}

UPDATE Here is how I am writing the data to the database, It's a Spring Batch 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);
        }
    }
}

CodePudding user response:

First of all thanks to all who tried to help!

Reading the resources that @Benjamin Maurer provided:

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

The most common Parent – Child association consists of a one-to-many and a many-to-one relationship, where the cascade being useful for the one-to-many side only

As the relation I have is ManyToOne it was really not useful to use the cascade and doesn't serve my need.

I used a different approache to reach the goal. I have created a service where it validates the existence of a city, then adds a new city if it does not exist.

@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;
    }
}

CodePudding user response:

Hibernate will use the @Id property of City to determine if it is new or not. When it is null, Hibernate couldn't possibly know that a similar entry already exists. So you need to perform a query to find each city first:

    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);
    }

If you use Hibernate and City.identification is unique and always non-null, you can use it as a NaturalID:

In City:

@NaturalId
private String identification;

Then:

    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);
    }

But if you do have City.id set, i.e., not null, you can use EntityManager.merge to get a managed entity:

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

        em.persist(history);
    }

One more remark: We are not in the relational domain, but we are mapping object graphs. So calling your fields cityId and municipalityId is arguably wrong - even the type says so: City cityId. They are not just plain identifiers, but full fledged objects: City city.

  • Related