-1

I'm using JPA with Hibernate and spring-boot-starter-data-jpa. I want to merge/update a set of Items. If it's a new item, I want to persist, if it's an already existsing item i want to update it.

    @PersistenceContext(unitName = "itemEntityManager")
    private EntityManager em;

    @Transactional
    public void saveItems(Set<Item>> items) {
        items.forEach(em::merge);
    }

When I try it like this, every Item creates a new HQL statment and it's inperformant. So I'm looking for a way to save all Items in one time (if it's possible), to save calls and time.

I found this:

        EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        items.forEach(em::merge);
        transaction.commit();

but i can't use this transaction because i use the @Transactional. Is there a way with native SQL?

balias
  • 499
  • 1
  • 4
  • 17
Lexuna
  • 7
  • 6
  • What exactly are you trying to do? Are you sure you want to `merge`? That means "items" contains _detached_ entities and you want to bring their state back into the persistence context. Hibernate will need one (or more) query per item not already in the cache and one (or more) updates per item. (There are ways to improve perf though, e.g., statement batching and in _some cases_ hibernate specific `update` method) – Benjamin Maurer Oct 07 '21 at 10:25
  • I want to save the Item if it's not already ther and update if it's already exists – Lexuna Oct 07 '21 at 10:35

2 Answers2

0

You could use @SQLInsert for this purpose to use batch inserts. See Hibernate Transactions and Concurrency Using attachDirty (saveOrUpdate)

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58
-2

I created a native SQL statement:

  1. I joined all items as a list of values in sql format
String sqlValues = String.join(",", items.stream().map(this::toSqlEntry).collect(Collectors.toSet()));
  1. Then i called a native query
    em.createNativeQuery("INSERT INTO config_item"
                + "( id, eaid, name, type, road, offs, created, deleted )"
                + " VALUES " + sqlValues
                + " ON CONFLICT (id) DO UPDATE "
                + " SET "
                    + "eaid=excluded.eaid,\n"
                    + "name=excluded.name,\n"
                    + "type=excluded.type,\n"
                    + "road=excluded.road,\n"
                    + "offs=excluded.offs,\n"
                    + "created=excluded.created,\n"
                    + "deleted=excluded.deleted;"
        ).executeUpdate();

That’s a lot faster and works

Lexuna
  • 7
  • 6